gem_dating 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 240b4b51a3ff7ea51e8f5133c5980b857656499d37cb9dca2e427f084f5d8e9f
4
- data.tar.gz: 3bee7a49cb6cff56b5ca793cd49103c2e16f60a74dcb8c98313e83c4c3bde008
3
+ metadata.gz: 50f8a606d682704f10a2ac5378c25a792d4c336ad08e05319e28333dc8ddfcb1
4
+ data.tar.gz: e8bc47b72796b1db95d67de3602d4bc6f27907807f80ad441b9f31faa7409596
5
5
  SHA512:
6
- metadata.gz: 350ac78f61900661889eeb66bb3f4ccf9d54d752d8d9034aa910e40827aa3faf0ff8e96c67db75df8a67413e8f18dcdae3359685a45a48d26e68f67133228962
7
- data.tar.gz: b03e680f2824c6e8a6a67bc3a8664946e00e5bf881ce1a4977b9e851b5a0596806b55abd6c6272ce737e43c5e499e276e2bb7f3784001c5533c1ad3ffa3cef86
6
+ metadata.gz: abe1fdb44e970f1b0f0ae005104109da3f4cceb8124e081f81b4fc8a4727df70d0f51186879f96e7451c575aad4449fdeec2b9ef37790e695a1a0f5fe3f14839
7
+ data.tar.gz: f07e00ddc9906d3a5e378971bc09bbc709b2ba131b9740cbe0cb329c055a1283fb4536be6dbf110255d56860d72d0ac79f1768cab62763116472c511a806821b
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gem_dating (0.1.0)
4
+ gem_dating (0.1.2)
5
5
  bundler
6
6
  table_print
7
7
 
@@ -57,6 +57,8 @@ GEM
57
57
 
58
58
  PLATFORMS
59
59
  arm64-darwin-22
60
+ arm64-darwin-23
61
+ arm64-darwin-24
60
62
  x86_64-linux
61
63
 
62
64
  DEPENDENCIES
data/README.md CHANGED
@@ -1,47 +1,68 @@
1
1
  # GemDating
2
2
 
3
- GemDating is a library for determining the relative age of a set of gems.
3
+ `gem_dating` is a library for determining the relative age of a set of gems.
4
4
 
5
- The primary use case is when evaluating a codebase for upgrades - a gem from 2017 may effectively be abandoned and could
6
- cause trouble if you're targeting an upgrade to Ruby 4.1
5
+ The primary use case is when evaluating a codebase for upgrades - a gem from 2017 may effectively be abandoned and could cause trouble if you're targeting an upgrade to Ruby 4.1
7
6
 
8
7
  ## Usage
9
8
 
10
- ### Installation
9
+ If you have rubygems 3.4.8 or later installed, you can skip installation and just run via `gem exec gem_dating [[path/to/Gemfile]`
10
+
11
+
12
+ ### Gem Installation
11
13
 
12
14
  `gem install gem_dating` or add it to your Gemfile:
13
15
 
14
16
  ```ruby
15
- gem 'gem_dating', group: [:development]
17
+ gem 'gem_dating', group: [:development], require: false
16
18
  ```
17
19
 
18
- ### Running GemDating
20
+ ### Command Line Options
19
21
 
20
- This gem provides a *very* limited command line interface. It may be invoked with:
22
+ GemDating supports several command line options to customize its behavior:
21
23
 
22
- ```bash
23
- $ gem_dating [path/to/Gemfile]
24
24
  ```
25
+ gem_dating [OPTIONS] [<GEMFILE_FILEPATH>]
26
+ ```
27
+
28
+ #### Available Options:
25
29
 
26
- Given a path to a Gemfile, GemDating will output a list of gems and their relative ages to the stdout stream.
30
+ - `--help`, `-h`, `-?`: Show the help message
31
+ - `--older-than=<AGE>`, `--ot=<AGE>`: Filter gems updated within the last X time period
32
+ - Examples: `--older-than=2y` (2 years), `--ot=1m` (1 month), `--ot=4w` (4 weeks), `--ot=10d` (10 days)
33
+ - `--sort-by=<FIELD>`: Sort by field
34
+ - Available fields: `name` or `date`
35
+ - Default: `name`
36
+ - `--order=<DIRECTION>`: Sort direction
37
+ - Available directions: `asc` (ascending) or `desc` (descending)
38
+ - Default: `asc`
39
+ - `--json`: Output results as JSON instead of table format
27
40
 
28
- For example:
41
+ #### Examples:
29
42
 
30
43
  ```bash
31
- $ gem_dating ~/code/my_app/Gemfile
32
- ```
33
- to read the output in your terminal.
44
+ # Show gems older than 1 year
45
+ $ gem_dating --older-than=1y
46
+
47
+ # Sort gems by date in descending order (newest first)
48
+ $ gem_dating --sort-by=date --order=desc
34
49
 
35
- Or you can run
50
+ # Output results as JSON
51
+ $ gem_dating --json
52
+
53
+ # Combine multiple options
54
+ $ gem_dating ~/code/my_app/Gemfile --older-than=6m --sort-by=date --order=desc
55
+ ```
56
+
57
+ GemDating leans on `$stdout`, so you can pipe the output to a file if you'd like:
36
58
  ```bash
37
59
  $ gem_dating ~/code/my_app/Gemfile > ~/code/my_app/gem_ages.txt
38
60
  ```
39
- which will pipe the output into a text file.
40
61
 
41
- The command line output will look something like this:
62
+ [TablePrint](https://github.com/arches/table_print) formats the output to something like this:
42
63
 
43
64
  ```bash
44
- NAME | VERSION | DATE
65
+ NAME | VERSION | DATE
45
66
  ------------|---------|-----------
46
67
  rest-client | 2.1.0 | 2019-08-21
47
68
  rails | 7.0.5 | 2023-05-24
@@ -53,7 +74,7 @@ passing in a string of a gem name, or a path to a Gemfile. You can then parse th
53
74
  a minimal hash, or the Table output you'd see in the CLI.
54
75
 
55
76
  ```ruby
56
- # irb
77
+ # irb
57
78
  # #:001 >
58
79
 
59
80
  require "gem_dating"
@@ -64,22 +85,32 @@ more_dating = GemDating.from_file("path/to/Gemfile")
64
85
 
65
86
 
66
87
  dating.to_a
67
- # => [Gem::Specification.new do |s|
88
+ # => [Gem::Specification.new do |s|
68
89
  # s.name = "rails"
69
90
  # s.version = Gem::Version.new("7.0.5")
70
91
  # s.installed_by_version = Gem::Version.new("0") ...etc
71
92
 
72
93
  dating.to_h
73
- # => {"rails"=>{"name"=>"rails", "version"=>"7.0.5", "date"=>"2023-05-24"}}
94
+ # => {"rails"=>{"name"=>"rails", "version"=>"7.0.5", "date"=>"2023-05-24"}}
74
95
 
75
96
  more_dating.table_print
76
- # =>
97
+ # =>
77
98
  # NAME | VERSION | DATE
78
99
  # ------------|---------|-----------
79
100
  # rails | 7.0.5 | 2023-05-24
80
101
  # ...etc
81
102
  ```
82
103
 
104
+ ### Caveats
105
+
106
+ `gem_dating` avoids utilizing Bundler, and intends to be useful where you want to evaluate a set of gems without
107
+ the overhead of a full bundle install. If you've got a valid bundle you should consider the built in
108
+ [bundle-outdated](https://bundler.io/v2.4/man/bundle-outdated.1.html), or other available tools for interacting
109
+ with your Gemfile, like [libyear-bundler](https://github.com/jaredbeck/libyear-bundler).
110
+
111
+ Sometimes we [get incorrect dates](https://github.com/testdouble/gem_dating/issues/10).
112
+ TLDR; sometimes GemSpecs are built in an environment where the date gets overwritten. That'll be up to the gem
113
+ owner(s) to determine if they'd like to adjust it to provide accurate dates.
83
114
 
84
115
 
85
116
  ## Code of Conduct
data/Rakefile CHANGED
@@ -7,4 +7,4 @@ Rake::TestTask.new(:test) do |t|
7
7
  t.test_files = FileList["test/**/*_test.rb"]
8
8
  end
9
9
 
10
- task :default => :test
10
+ task default: :test
data/gem_dating.gemspec CHANGED
@@ -1,27 +1,26 @@
1
-
2
1
  lib = File.expand_path("../lib", __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require "gem_dating/version"
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "gem_dating"
8
- spec.version = GemDating::VERSION
9
- spec.authors = ["Steve Jackson", "Daniel Huss"]
10
- spec.email = ["steve@testdouble.com"]
6
+ spec.name = "gem_dating"
7
+ spec.version = GemDating::VERSION
8
+ spec.authors = ["Steve Jackson", "Daniel Huss"]
9
+ spec.email = ["steve@testdouble.com"]
11
10
 
12
- spec.summary = "How old is that anyway?"
13
- spec.homepage = "https://github.com/testdouble/gem_dating"
14
- spec.license = "MIT"
11
+ spec.summary = "How old is that anyway?"
12
+ spec.homepage = "https://github.com/testdouble/gem_dating"
13
+ spec.license = "MIT"
15
14
 
16
15
  spec.metadata["homepage_uri"] = spec.homepage
17
16
  spec.metadata["source_code_uri"] = spec.homepage
18
17
  spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
19
18
 
20
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
+ spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
21
20
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
21
  end
23
- spec.bindir = "exe"
24
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
24
  spec.require_paths = ["lib"]
26
25
 
27
26
  spec.add_dependency "bundler"
@@ -3,21 +3,73 @@ module GemDating
3
3
  SUCCESS = 0
4
4
  GENERAL_ERROR = 1
5
5
 
6
- def initialize(argv)
7
- @argv = argv
6
+ HELP_TEXT =
7
+ <<~HELP
8
+ gem_dating [--help | -h] [<GEMFILE_FILEPATH>]
9
+
10
+ GEMFILE_FILEPATH defaults to ./Gemfile if not provided.
11
+
12
+ Options:
13
+ --help, -h, -? Show this help message
14
+ --older-than=<AGE>, --ot=<AGE> Filter gems updated within the last X (e.g. 2y, 1m, 4w, 10d)
15
+ --sort-by=<FIELD> Sort by field (name or date), defaults to name
16
+ --order=<DIRECTION> Sort direction (asc or desc), defaults to asc
17
+ --json Output results as JSON
18
+ HELP
19
+
20
+ def initialize(argv = [])
21
+ args, file_path = argv.partition { |arg| (arg =~ /--\w+/) || (arg =~ /(-[a-z])/) }
22
+
23
+ @args = args
24
+ @file_path = file_path.first
25
+ @options = parse_args
8
26
  end
9
27
 
10
28
  def run
11
- if @argv.empty?
12
- $stdout << "Usage: gem_dating [GEMFILE_FILEPATH]\n"
13
- return GENERAL_ERROR
29
+ if @options[:help]
30
+ $stdout << HELP_TEXT
31
+ return SUCCESS
14
32
  end
15
33
 
16
- path = @argv.first
34
+ unless @file_path
35
+ current_directory = Dir.pwd
36
+ file_path = File.join(current_directory, "Gemfile")
17
37
 
18
- $stdout << GemDating.from_file(path).table_print << "\n"
38
+ if File.exist?(file_path)
39
+ @file_path = file_path
40
+ else
41
+ $stdout << HELP_TEXT
42
+ return GENERAL_ERROR
43
+ end
44
+ end
45
+
46
+ result = GemDating.from_file(@file_path, @options)
47
+ output = @options[:json] ? result.to_json : result.table_print
48
+ $stdout << output << "\n"
19
49
 
20
50
  SUCCESS
21
51
  end
52
+
53
+ private
54
+
55
+ def parse_args(args = @args)
56
+ options = {}
57
+ options[:help] = true if (args & %w[-h --help -?]).any?
58
+ options[:json] = true if args.include?("--json")
59
+
60
+ if (older_than = args.find { |arg| arg.start_with?("--older-than=", "--ot=") })
61
+ options[:older_than] = older_than.split("=").last
62
+ end
63
+
64
+ if (sort_by = args.find { |arg| arg.start_with?("--sort-by=") })
65
+ options[:sort_by] = sort_by.split("=").last
66
+ end
67
+
68
+ if (order = args.find { |arg| arg.start_with?("--order=") })
69
+ options[:order] = order.split("=").last
70
+ end
71
+
72
+ options
73
+ end
22
74
  end
23
75
  end
@@ -19,17 +19,18 @@ module GemDating
19
19
  lines.each_with_object([]) do |line, gems|
20
20
  line = gem_line(line.strip)
21
21
  gems << cleanup(line) if line
22
- end
22
+ end.uniq
23
23
  end
24
24
 
25
25
  def gem_line(line)
26
- return if line.strip == "end"
26
+ single_word_ruby_statements = %w[end else # gemspec]
27
+ return if single_word_ruby_statements.include? line.strip
27
28
 
28
29
  if line.start_with? "gem("
29
30
  line.split("(")[1].split(",")[0]
30
31
  elsif line.start_with? "gem"
31
32
  line.split[1].split(",")[0]
32
- elsif line.split.length == 1
33
+ elsif line.split.length == 1 # match "just" gemname
33
34
  line
34
35
  end
35
36
  end
@@ -1,4 +1,5 @@
1
1
  require "table_print"
2
+ require "json"
2
3
 
3
4
  module GemDating
4
5
  class Result
@@ -24,5 +25,57 @@ module GemDating
24
25
  def table_print
25
26
  TablePrint::Printer.table_print(specs, [:name, :version, {date: {time_format: "%Y-%m-%d", width: 10}}]).encode("utf-8")
26
27
  end
28
+
29
+ def to_json
30
+ JSON.generate(to_h)
31
+ end
32
+
33
+ def older_than(date)
34
+ specs.select! { |spec| spec.date.to_date < cut_off(date) }
35
+ self
36
+ end
37
+
38
+ def sort(options = {})
39
+ field = options[:sort_by] || "name"
40
+ direction = options[:order] || "asc"
41
+
42
+ @specs = @specs.sort_by do |spec|
43
+ case field
44
+ when "name"
45
+ spec.name.downcase
46
+ when "date"
47
+ spec.date
48
+ else
49
+ spec.name.downcase
50
+ end
51
+ end
52
+
53
+ @specs = @specs.reverse if direction.downcase == "desc"
54
+
55
+ self
56
+ end
57
+
58
+ private
59
+
60
+ def cut_off(date)
61
+ return unless date
62
+ curr_date = Date.today
63
+
64
+ number = date[0..-2].to_i
65
+ unit = date[-1]
66
+
67
+ case unit
68
+ when "y"
69
+ curr_date << (12 * number)
70
+ when "m"
71
+ curr_date << number
72
+ when "w"
73
+ curr_date - (number * 7)
74
+ when "d"
75
+ curr_date - number
76
+ else
77
+ raise ArgumentError, "Invalid date format: #{date}"
78
+ end
79
+ end
27
80
  end
28
81
  end
@@ -1,3 +1,3 @@
1
1
  module GemDating
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.2"
3
3
  end
data/lib/gem_dating.rb CHANGED
@@ -5,15 +5,23 @@ require_relative "gem_dating/result"
5
5
  require_relative "gem_dating/cli"
6
6
 
7
7
  module GemDating
8
- def self.from_string(s)
8
+ def self.from_string(s, options = {})
9
9
  gems = Input.string(s).gems
10
- specs = Rubygems.fetch(gems)
11
- Result.new(specs)
10
+ fetch_specs(gems, options)
12
11
  end
13
12
 
14
- def self.from_file(path)
13
+ def self.from_file(path, options = {})
15
14
  gems = Input.file(path).gems
15
+ fetch_specs(gems, options)
16
+ end
17
+
18
+ def self.fetch_specs(gems, options)
16
19
  specs = Rubygems.fetch(gems)
17
- Result.new(specs)
20
+ results = Result.new(specs)
21
+ results.older_than(options[:older_than]) if options[:older_than]
22
+ results.sort(options)
23
+ results
18
24
  end
25
+
26
+ private_class_method :fetch_specs
19
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_dating
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Jackson
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-07-19 00:00:00.000000000 Z
12
+ date: 2025-06-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -88,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  requirements: []
91
- rubygems_version: 3.4.6
91
+ rubygems_version: 3.4.10
92
92
  signing_key:
93
93
  specification_version: 4
94
94
  summary: How old is that anyway?