diff --git a/Dockerfile b/Dockerfile index 9bc4a61..2e8c4c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,7 @@ ENV GEM_HOME=${LAMBDA_TASK_ROOT} COPY Gemfile Gemfile.lock ./ RUN bundle install -COPY lib/ . +COPY lib/ lib/ COPY tracks/ tracks/ -CMD [ "app.Exercism::CountLinesOfCode.process" ] +CMD [ "lib/lines_of_code_counter.LinesOfCodeCounter.process" ] diff --git a/bin/run-in-docker.sh b/bin/run-in-docker.sh index 6533b48..327f146 100755 --- a/bin/run-in-docker.sh +++ b/bin/run-in-docker.sh @@ -31,10 +31,8 @@ container_port=9876 # Create the output directory if it doesn't exist mkdir -p "${output_dir}" -echo "${track_slug}/${exercise_slug}: counting lines of code..." - # Build the Docker image -docker build --rm -t exercism/lines-of-code-counter . +docker build --progress=plain --rm -t exercism/lines-of-code-counter . # Run the Docker image using the settings mimicking the production environment container_id=$(docker run \ @@ -44,10 +42,12 @@ container_id=$(docker run \ --mount type=bind,src="${output_dir}",dst=/output \ exercism/lines-of-code-counter) +echo "${track_slug}/${exercise_slug}: counting lines of code..." + # Call the function with the correct JSON event payload event_json=$(jq -n --arg t "${track_slug}" --arg e "${exercise_slug}" '{track: $t, exercise: $e, solution: "/solution", output: "/output"}') curl --silent --output /dev/null -XPOST http://localhost:${container_port}/2015-03-31/functions/function/invocations -d "${event_json}" -docker stop $container_id > /dev/null +echo "${track_slug}/${exercise_slug}: done" -echo "${track_slug}/${exercise_slug}: done" \ No newline at end of file +docker stop $container_id > /dev/null diff --git a/bin/run-tests-in-docker.sh b/bin/run-tests-in-docker.sh index 060d616..b6c4774 100755 --- a/bin/run-tests-in-docker.sh +++ b/bin/run-tests-in-docker.sh @@ -37,6 +37,8 @@ for test_dir in tests/${track_slug}/${exercise_slug}; do counts_file_path="${test_dir_path}/counts.json" expected_counts_file_path="${test_dir_path}/expected_counts.json" + rm -rf "${counts_file_path}" + bin/run-in-docker.sh "${track_name}" "${exercise_name}" "${test_dir_path}" "${test_dir_path}" echo "${track_name}/${exercise_name}: comparing counts.json to expected_counts.json" diff --git a/bin/run-tests.sh b/bin/run-tests.sh index 19143ae..6d46daa 100755 --- a/bin/run-tests.sh +++ b/bin/run-tests.sh @@ -36,6 +36,8 @@ for test_dir in tests/${track_slug}/${exercise_slug}; do counts_file_path="${test_dir_path}/counts.json" expected_counts_file_path="${test_dir_path}/expected_counts.json" + rm -rf "${counts_file_path}" + bin/run.sh "${track_name}" "${exercise_name}" "${test_dir_path}" "${test_dir_path}" echo "${track_name}/${exercise_name}: comparing counts.json to expected_counts.json" diff --git a/bin/run.rb b/bin/run.rb index 972d88f..03e97e6 100755 --- a/bin/run.rb +++ b/bin/run.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -require("./lib/app") +require("./lib/lines_of_code_counter") event = { "track" => ARGV[0], @@ -8,4 +8,4 @@ "solution" => ARGV[2], "output" => ARGV[3] } -Exercism::CountLinesOfCode.process(event: event, context: {}) +LinesOfCodeCounter.process(event: event, context: {}) diff --git a/lib/app.rb b/lib/app.rb deleted file mode 100644 index f7ed0eb..0000000 --- a/lib/app.rb +++ /dev/null @@ -1,91 +0,0 @@ -require "mandate" -require "json" -require "fileutils" - -module Exercism - # TODO: refactor (extract classes, rename to handler) - class CountLinesOfCode - include Mandate - - initialize_with :event, :content - - def call - File.write(output_ignore_file, output_ignore) - report = JSON.parse(`tokei #{solution_dir} --output json`, symbolize_names: true) - output = { - code: report[:Total][:code], - blanks: report[:Total][:blanks], - comments: report[:Total][:comments], - files: report[:Total][:children].values. - flatten. - map do |c| - c[:name]. - delete_prefix("#{solution_dir}/") - end. - sort. - to_a - } - File.write(output_counts_file, output.to_json) - FileUtils.rm(output_ignore_file) - end - - def self.process(event:, context:) - Exercism::CountLinesOfCode.(event, context) - end - - private - def track - event["track"] - end - - def solution_dir - event["solution"] - end - - def output_dir - event["output"] - end - - def output_counts_file - File.join(output_dir, "counts.json") - end - - def track_file - "tracks/#{track}.ignore" - end - - def exercise_config_file - File.join(solution_dir, ".meta", "config.json") - end - - memoize - def exercise_config - JSON.parse(File.read(exercise_config_file), symbolize_names: true) - end - - def output_ignore - if File.exist?(track_file) - [ - *File.readlines(track_file), - *exercise_config[:files][:test].to_a, - *exercise_config[:files][:example].to_a, - *exercise_config[:files][:exemplar].to_a, - *exercise_config[:files][:editor].to_a, - "counts.json", - "expected_counts.json" - ].compact.join("\n") - else - [ - "*", - *exercise_config[:files][:solution].to_a.map {|s|"!#{s}"}, - "counts.json", - "expected_counts.json" - ].compact.join("\n") - end - end - - def output_ignore_file - File.join(solution_dir, ".tokeignore") - end - end -end diff --git a/lib/lines_of_code_counter.rb b/lib/lines_of_code_counter.rb new file mode 100644 index 0000000..d3dae7b --- /dev/null +++ b/lib/lines_of_code_counter.rb @@ -0,0 +1,13 @@ +require "json" +require "mandate" +require "fileutils" +require "./lib/lines_of_code_counter/count_lines_of_code" +require "./lib/lines_of_code_counter/exercise" +require "./lib/lines_of_code_counter/ignore_file" +require "./lib/lines_of_code_counter/process_request" + +module LinesOfCodeCounter + def self.process(event:, context:) + ProcessRequest.(event, context) + end +end diff --git a/lib/lines_of_code_counter/count_lines_of_code.rb b/lib/lines_of_code_counter/count_lines_of_code.rb new file mode 100644 index 0000000..b4e163f --- /dev/null +++ b/lib/lines_of_code_counter/count_lines_of_code.rb @@ -0,0 +1,40 @@ +class CountLinesOfCode + include Mandate + + initialize_with :exercise + + def call + { + code: code, + blanks: blanks, + comments: comments, + files: files + } + end + + private + memoize + def report + JSON.parse(`tokei #{exercise.dir} --output json`, symbolize_names: true) + end + + def code + report[:Total][:code] + end + + def blanks + report[:Total][:blanks] + end + + def comments + report[:Total][:comments] + end + + def files + report[:Total][:children].values. + flatten. + map {|child| child[:name].delete_prefix("#{exercise.dir}/") }. + sort. + to_a + end +end diff --git a/lib/lines_of_code_counter/exercise.rb b/lib/lines_of_code_counter/exercise.rb new file mode 100644 index 0000000..cadf8ca --- /dev/null +++ b/lib/lines_of_code_counter/exercise.rb @@ -0,0 +1,36 @@ +class Exercise + include Mandate + + attr_reader :dir + + def initialize(dir) + @dir = dir + end + + def solution_files + config[:files][:solution].to_a + end + + def test_files + config[:files][:test].to_a + end + + def editor_files + config[:files][:editor].to_a + end + + def exemplar_files + config[:files][:exemplar].to_a + end + + def example_files + config[:files][:example].to_a + end + + private + memoize + def config + filepath = File.join(dir, ".meta", "config.json") + JSON.parse(File.read(filepath), symbolize_names: true) + end +end diff --git a/lib/lines_of_code_counter/ignore_file.rb b/lib/lines_of_code_counter/ignore_file.rb new file mode 100644 index 0000000..6df4b0d --- /dev/null +++ b/lib/lines_of_code_counter/ignore_file.rb @@ -0,0 +1,43 @@ +class IgnoreFile + include Mandate + + initialize_with :track, :exercise + + def filepath + File.join(exercise.dir, ".tokeignore") + end + + def content + rules = track_specific_rules? ? track_specific_rules : default_rules + rules.compact.join("\n") + end + + memoize + def track_specific_rules? + File.exist?(track_ignore_filepath) + end + + def track_specific_rules + [ + *File.readlines(track_ignore_filepath), + *exercise.test_files, + *exercise.example_files, + *exercise.exemplar_files, + *exercise.editor_files, + "counts.json", + "expected_counts.json" + ] + end + + def default_rules + [ + "*", + *exercise.solution_files.map {|s|"!#{s}"} + ] + end + + private + def track_ignore_filepath + "tracks/#{track}.ignore" + end +end diff --git a/lib/lines_of_code_counter/process_request.rb b/lib/lines_of_code_counter/process_request.rb new file mode 100644 index 0000000..9fff4c4 --- /dev/null +++ b/lib/lines_of_code_counter/process_request.rb @@ -0,0 +1,39 @@ +class ProcessRequest + include Mandate + + initialize_with :event, :content + + def call + File.write(ignore_file.filepath, ignore_file.content) + counts = CountLinesOfCode.(exercise) + File.write(counts_filepath, counts.to_json) + # FileUtils.rm(ignore_file.filepath) + end + + private + def track + event["track"] + end + + def solution_dir + event["solution"] + end + + def output_dir + event["output"] + end + + def counts_filepath + File.join(output_dir, "counts.json") + end + + memoize + def exercise + Exercise.new(solution_dir) + end + + memoize + def ignore_file + IgnoreFile.new(track, exercise) + end +end