diff --git a/lib/gem_dating.rb b/lib/gem_dating.rb index b22e4ef..0a5d78c 100644 --- a/lib/gem_dating.rb +++ b/lib/gem_dating.rb @@ -20,6 +20,7 @@ def self.fetch_specs(gems, options) specs = Rubygems.fetch(gems) results = Result.new(specs) results.older_than(options[:older_than]) if options[:older_than] + results.sort(options) results end diff --git a/lib/gem_dating/cli.rb b/lib/gem_dating/cli.rb index f8ebb4f..2aa1506 100644 --- a/lib/gem_dating/cli.rb +++ b/lib/gem_dating/cli.rb @@ -12,6 +12,9 @@ class Cli Options: --help, -h, -? Show this help message --older-than=, --ot= Filter gems updated within the last X (e.g. 2y, 1m, 4w, 10d) + --sort-by= Sort by field (name or date), defaults to name + --order= Sort direction (asc or desc), defaults to asc + --json Output results as JSON HELP def initialize(argv = []) @@ -40,7 +43,9 @@ def run end end - $stdout << GemDating.from_file(@file_path, @options).table_print << "\n" + result = GemDating.from_file(@file_path, @options) + output = @options[:json] ? result.to_json : result.table_print + $stdout << output << "\n" SUCCESS end @@ -50,9 +55,20 @@ def run def parse_args(args = @args) options = {} options[:help] = true if (args & %w[-h --help -?]).any? + options[:json] = true if args.include?("--json") + if (older_than = args.find { |arg| arg.start_with?("--older-than=", "--ot=") }) options[:older_than] = older_than.split("=").last end + + if (sort_by = args.find { |arg| arg.start_with?("--sort-by=") }) + options[:sort_by] = sort_by.split("=").last + end + + if (order = args.find { |arg| arg.start_with?("--order=") }) + options[:order] = order.split("=").last + end + options end end diff --git a/lib/gem_dating/result.rb b/lib/gem_dating/result.rb index 126a923..4ee131b 100644 --- a/lib/gem_dating/result.rb +++ b/lib/gem_dating/result.rb @@ -1,4 +1,5 @@ require "table_print" +require "json" module GemDating class Result @@ -25,8 +26,33 @@ def table_print TablePrint::Printer.table_print(specs, [:name, :version, {date: {time_format: "%Y-%m-%d", width: 10}}]).encode("utf-8") end + def to_json + JSON.generate(to_h) + end + def older_than(date) specs.select! { |spec| spec.date.to_date < self.cut_off(date) } + self + end + + def sort(options = {}) + field = options[:sort_by] || "name" + direction = options[:order] || "asc" + + @specs = @specs.sort_by do |spec| + case field + when "name" + spec.name.downcase + when "date" + spec.date + else + spec.name.downcase + end + end + + @specs = @specs.reverse if direction.downcase == "desc" + + self end private diff --git a/test/cli_test.rb b/test/cli_test.rb index 73911f5..83261f9 100644 --- a/test/cli_test.rb +++ b/test/cli_test.rb @@ -42,8 +42,8 @@ def test_gemfile NAME | VERSION | DATE ----------------|----------|----------- banana-client | 21.1.0 | 1990-08-21 - rails-on-rubies | 70.0.5 | 2123-05-24 giraffeql | 0.0.2227 | 2023-05-17 + rails-on-rubies | 70.0.5 | 2123-05-24 EXPECTED assert_equal 0, exit_code @@ -64,8 +64,8 @@ def test_default_to_existing_relative_gemfile NAME | VERSION | DATE ----------------|----------|----------- banana-client | 21.1.0 | 1990-08-21 - rails-on-rubies | 70.0.5 | 2123-05-24 giraffeql | 0.0.2227 | 2023-05-17 + rails-on-rubies | 70.0.5 | 2123-05-24 EXPECTED assert_equal 0, exit_code @@ -114,7 +114,77 @@ def test_parse_args cli = GemDating::Cli.new(["--help", "--older-than=2y"]) assert_equal({ help: true, older_than: "2y" }, cli.send(:parse_args)) + cli = GemDating::Cli.new(["--json"]) + assert_equal({ json: true }, cli.send(:parse_args)) + cli = GemDating::Cli.new([]) assert_equal({}, cli.send(:parse_args)) end + + def test_sort_by_name_asc + exit_code = nil + + stdout, _stderr = capture_io do + exit_code = GemDating::Cli.new(["test/Gemfile.example", "--sort-by=name", "--order=asc"]).run + end + + expected_out = <<~EXPECTED + NAME | VERSION | DATE + ----------------|----------|----------- + banana-client | 21.1.0 | 1990-08-21 + giraffeql | 0.0.2227 | 2023-05-17 + rails-on-rubies | 70.0.5 | 2123-05-24 + EXPECTED + + assert_equal 0, exit_code + assert_equal expected_out, stdout + end + + def test_sort_by_date_desc + exit_code = nil + + stdout, _stderr = capture_io do + exit_code = GemDating::Cli.new(["test/Gemfile.example", "--sort-by=date", "--order=desc"]).run + end + + expected_out = <<~EXPECTED + NAME | VERSION | DATE + ----------------|----------|----------- + rails-on-rubies | 70.0.5 | 2123-05-24 + giraffeql | 0.0.2227 | 2023-05-17 + banana-client | 21.1.0 | 1990-08-21 + EXPECTED + + assert_equal 0, exit_code + assert_equal expected_out, stdout + end + + def test_json_output + exit_code = nil + + stdout, _stderr = capture_io do + exit_code = GemDating::Cli.new(["test/Gemfile.example", "--json"]).run + end + + expected_data = { + "banana-client" => { + "name" => "banana-client", + "version" => "21.1.0", + "date" => "1990-08-21" + }, + "giraffeql" => { + "name" => "giraffeql", + "version" => "0.0.2227", + "date" => "2023-05-17" + }, + "rails-on-rubies" => { + "name" => "rails-on-rubies", + "version" => "70.0.5", + "date" => "2123-05-24" + } + } + + assert_equal 0, exit_code + assert_equal expected_data, JSON.parse(stdout) + end end diff --git a/test/gem_dating_test.rb b/test/gem_dating_test.rb index 014ccd2..d44dd2e 100644 --- a/test/gem_dating_test.rb +++ b/test/gem_dating_test.rb @@ -32,7 +32,8 @@ def test_gems_copy_pasted_from_gemfile gem "puma", "~> 5.0" TEXT - rails, _rest = GemDating.from_string(pasteboard).to_a + specs = GemDating.from_string(pasteboard).to_a + rails = specs.find { |spec| spec.name == "rails" } assert_equal "rails", rails.name assert_operator Gem::Version.new("7.0"), :<=, rails.version diff --git a/test/result_test.rb b/test/result_test.rb index 23f3f18..5c9f928 100644 --- a/test/result_test.rb +++ b/test/result_test.rb @@ -58,6 +58,22 @@ def test_hash assert_equal expected, @result.to_h end + def test_json + expected = { + "hi" => { + "name" => "hi", + "version" => "42.42", + "date" => "2015-09-18" + }, + "there" => { + "name" => "there", + "version" => "1.27.0.01", + "date" => "2009-09-02" + } + } + assert_equal expected, JSON.parse(@result.to_json) + end + def test_table expected = <<-TEXT NAME | VERSION | DATE @@ -110,4 +126,40 @@ def test_cut_off_invalid_format_raises assert_raises(ArgumentError) { @date_result.older_than("abc") } assert_raises(ArgumentError) { @date_result.older_than("") } end + + def test_sort_by_name_asc + result = GemDating::Result.new([@spec1, @spec2]) + sorted = result.sort(sort_by: "name", order: "asc") + + assert_equal ["hi", "there"], sorted.to_a.map(&:name) + end + + def test_sort_by_name_desc + result = GemDating::Result.new([@spec1, @spec2]) + sorted = result.sort(sort_by: "name", order: "desc") + + assert_equal ["there", "hi"], sorted.to_a.map(&:name) + end + + def test_sort_by_date_asc + result = GemDating::Result.new([@spec1, @spec2]) + sorted = result.sort(sort_by: "date", order: "asc") + + assert_equal ["there", "hi"], sorted.to_a.map(&:name) + end + + def test_sort_by_date_desc + result = GemDating::Result.new([@spec1, @spec2]) + sorted = result.sort(sort_by: "date", order: "desc") + + assert_equal ["hi", "there"], sorted.to_a.map(&:name) + end + + def test_sort_defaults + result = GemDating::Result.new([@spec2, @spec1]) + sorted = result.sort + + # Default sort is by name in ascending order + assert_equal ["hi", "there"], sorted.to_a.map(&:name) + end end