latest 0.2.0 → 0.3.0

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.
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ### Latest keeps us up to speed by querying rubygems.org for a gem's most recent version number. Yep, that's all it does.
4
4
 
5
- Basically I was tired of visiting `https://rubygems.org/gems/whatever-gem` to find out a gem's most recent version number.
5
+ Basically I was tired of visiting `https://rubygems.org/gems/whatever-gem` to find out a gem's most recent version.
6
6
 
7
7
 
8
8
  ------------------------------------------------------------------------------
@@ -14,18 +14,24 @@ After installing, use latest from your command line like so:
14
14
 
15
15
  ```bash
16
16
  latest rails
17
- # rails 3.2.1
17
+ # rails 3.2.2 (41013 downloads)
18
18
 
19
19
  latest rails spree haml
20
- # rails 3.2.1
21
- # spree 1.0.0
22
- # haml 3.1.4
23
- ```
20
+ # rails 3.2.2 (41014 downloads)
21
+ # spree 1.0.1 (279 downloads)
22
+ # haml 3.1.4 (289029 downloads)
24
23
 
25
24
  latest rails spree --pre haml
26
- # rails 3.2.1
27
- # spree --pre 1.0.0.rc4
28
- # haml 3.1.4
25
+ # rails 3.2.2 (41015 downloads)
26
+ # spree --pre 1.0.0.rc4 (134 downloads)
27
+ # haml 3.1.4 (289029 downloads)
28
+ ```
29
+
30
+ You can print latest's version like so:
31
+
32
+ ```bash
33
+ latest -v
34
+ # Latest v0.3.0
29
35
  ```
30
36
 
31
37
 
@@ -46,7 +52,7 @@ sudo gem install latest
46
52
  Testing
47
53
  ------------------------------------------------------------------------------
48
54
 
49
- Testing is done with minitest. Run the tests with:
55
+ Testing is done with minitest/spec. Run the specs with:
50
56
 
51
57
  ```bash
52
58
  bundle exec rake
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
 
4
- Rake::TestTask.new
4
+ Rake::TestTask.new do |t|
5
+ t.test_files = FileList["spec/**/*_spec.rb"]
6
+ end
5
7
 
6
8
  task :default => [ :test ]
data/bin/latest CHANGED
@@ -2,25 +2,25 @@
2
2
  # encoding: UTF-8
3
3
 
4
4
  $:.push File.expand_path("../../lib", __FILE__)
5
-
6
5
  require "latest"
7
6
 
8
- if ARGV.first =~ /^-(v|-version)$/
7
+ if ARGV.empty?
8
+ puts Latest.usage
9
+ elsif ARGV.first =~ /^-(v|-version)$/
9
10
  puts "Latest v" + Latest::VERSION
10
11
  else
11
12
  ARGV.each_with_index do |name, index|
12
- print name + " "
13
+ print name + " "
13
14
  begin
14
- gem = Latest.lookup(name)
15
+ gem = Latest::Gem.new(name)
15
16
  if ARGV[index + 1] == "--pre"
16
17
  ARGV.slice!(index + 1, 1)
18
+ gem.pre = true
17
19
  print "--pre "
18
- puts gem.last_prerelease_version
19
- else
20
- puts gem.last_stable_version
21
20
  end
22
- rescue Latest::GemNotFoundError => e
23
- print "<- "
21
+ puts "#{gem.version} (#{gem.downloads} downloads)"
22
+ rescue StandardError => e
23
+ print "[Error] "
24
24
  puts e.message
25
25
  end
26
26
  end
data/latest.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Spencer Steffen"]
10
10
  s.email = ["spencer@citrusme.com"]
11
11
  s.homepage = "https://github.com/citrus/latest"
12
- s.summary = %q{Latest keeps us up to speed by querying rubygems.org for a gem's most recent version number. Yep, that's all it does.}
12
+ s.summary = %q{Latest queries rubygems.org for the latest version of a gem.}
13
13
  s.description = %q{Latest keeps us up to speed by querying rubygems.org for a gem's most recent version number. Yep, that's all it does.}
14
14
 
15
15
  s.rubyforge_project = "latest"
data/lib/latest/gem.rb CHANGED
@@ -5,49 +5,85 @@ require "json"
5
5
  module Latest
6
6
  class Gem
7
7
 
8
- attr_reader :name, :versions
8
+ class NameParseError < StandardError; end
9
+ class GemNotFoundError < StandardError; end
10
+ class RequestError < StandardError; end
11
+
12
+ attr_reader :name, :pre, :response, :downloads
9
13
 
10
14
  def initialize(name)
11
- @name = name
12
- @versions = fetch
13
- if @versions.nil? || @versions.empty?
14
- raise ::Latest::GemNotFoundError, "`#{name}` could not be found on rubygems.org!"
15
- end
15
+ @name, @pre = parse_name(name)
16
16
  self
17
17
  end
18
18
 
19
- def last_prerelease_version
20
- return @last_prerelease_version if @last_prerelease_version
21
- @last_prerelease_version = find_latest_version(true)
19
+ def fetch
20
+ @response = send_request
21
+ parse_response
22
22
  end
23
23
 
24
- def last_stable_version
25
- return @last_stable_version if @last_stable_version
26
- @last_stable_version = find_latest_version
24
+ def versions
25
+ @versions ||= fetch
27
26
  end
28
-
27
+
28
+ def version
29
+ @version ||= parse_versions
30
+ end
31
+
32
+ def pre=(value)
33
+ @pre = !!value
34
+ @version = parse_versions
35
+ end
36
+
29
37
  private
30
-
31
- def find_latest_version(prerelease=false)
32
- version = "(not found)"
33
- @versions.each do |v|
34
- next unless v["prerelease"] == prerelease
35
- version = v["number"]
36
- break
37
- end
38
- version
38
+
39
+ def url
40
+ @url ||= "https://rubygems.org/api/v1/versions/#{name}.json"
39
41
  end
40
-
41
- def fetch
42
- uri = URI("https://rubygems.org/api/v1/versions/#{@name}.json")
42
+
43
+ def uri
44
+ @uri ||= URI.parse(url)
45
+ end
46
+
47
+ def send_request
43
48
  Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http|
44
49
  request = Net::HTTP::Get.new uri.request_uri
45
- response = http.request request
46
- if response.is_a?(Net::HTTPSuccess)
47
- return JSON.parse(response.body)
48
- end
50
+ http.request(request)
51
+ end
52
+ end
53
+
54
+ def parse_name(name)
55
+ names = name.split(" ")
56
+ case names.length
57
+ when 1; [ names.first, false ]
58
+ when 2
59
+ return [ names.first, true ] if names.last == "--pre"
60
+ raise Latest::Gem::NameParseError.new("Invalid prerelease flag. Try `latest #{names.first} --pre`")
61
+ else raise Latest::Gem::NameParseError.new("Argument error, #{names.length} for 1-2")
49
62
  end
50
63
  end
51
64
 
65
+ def parse_response
66
+ case @response
67
+ when Net::HTTPSuccess
68
+ @versions = JSON.parse(@response.body)
69
+ when Net::HTTPNotFound
70
+ raise Latest::Gem::GemNotFoundError.new("`#{name}` could not be found on rubygems.org")
71
+ else
72
+ raise Latest::Gem::RequestError.new("An error occured while fetching `#{name}. Rubygems.org responded with status #{@response.code}")
73
+ end
74
+ @versions
75
+ end
76
+
77
+ def parse_versions
78
+ @version = "(not found)"
79
+ versions.each do |v|
80
+ next unless v["prerelease"] == pre
81
+ @version = v["number"]
82
+ @downloads = v["downloads_count"]
83
+ break
84
+ end
85
+ @version
86
+ end
87
+
52
88
  end
53
89
  end
@@ -1,3 +1,3 @@
1
1
  module Latest
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/latest.rb CHANGED
@@ -2,12 +2,9 @@ require "latest/version"
2
2
  require "latest/gem"
3
3
 
4
4
  module Latest
5
-
6
- class GemNotFoundError < StandardError; end
7
-
8
- def self.lookup(name)
9
- gem = Gem.new(name)
10
- gem
5
+
6
+ def self.usage
7
+ "Usage: latest [GEM [--pre]] [GEM2 [--pre]] etc"
11
8
  end
12
9
 
13
10
  end
@@ -0,0 +1,118 @@
1
+ require "latest"
2
+ require "minitest/autorun"
3
+
4
+ FAKE_VERSIONS = [
5
+ {"authors"=>"Bogus", "built_at"=>"2011-10-22T00:00:00Z", "description"=>"Bogus...", "downloads_count"=>10000, "number"=>"1.0.1", "summary"=>"Bogus", "platform"=>"ruby", "prerelease"=>false},
6
+ {"authors"=>"Bogus", "built_at"=>"2011-06-05T00:00:00Z", "description"=>"Bogus...", "downloads_count"=>1000, "number"=>"1.0.0", "summary"=>"Bogus", "platform"=>"ruby", "prerelease"=>false},
7
+ {"authors"=>"Bogus", "built_at"=>"2011-06-01T00:00:00Z", "description"=>"Bogus...", "downloads_count"=>100, "number"=>"1.0.0.rc1", "summary"=>"Bogus", "platform"=>"ruby", "prerelease"=>true},
8
+ {"authors"=>"Bogus", "built_at"=>"2011-05-20T00:00:00Z", "description"=>"Bogus...", "downloads_count"=>10, "number"=>"0.9.9", "summary"=>"Bogus", "platform"=>"ruby", "prerelease"=>false},
9
+ {"authors"=>"Bogus", "built_at"=>"2011-03-13T04:00:00Z", "description"=>"Bogus...", "downloads_count"=>1, "number"=>"0.9.9.rc1", "summary"=>"Bogus", "platform"=>"ruby", "prerelease"=>true}
10
+ ]
11
+
12
+ describe Latest::Gem do
13
+
14
+ describe "#initialize" do
15
+
16
+ it "should initialize with a name" do
17
+ @gem = Latest::Gem.new("rake")
18
+ @gem.name.must_equal "rake"
19
+ @gem.pre.must_equal false
20
+ end
21
+
22
+ it "should initialize with a name and pre" do
23
+ @gem = Latest::Gem.new("rake --pre")
24
+ @gem.name.must_equal "rake"
25
+ @gem.pre.must_equal true
26
+ end
27
+
28
+ it "should raise a name parse error when an invalid pre flag is given" do
29
+ %w(pre -pre PRE --PRE a 0 ?).each do |i|
30
+ lambda {
31
+ Latest::Gem.new("rake #{i}")
32
+ }.must_raise Latest::Gem::NameParseError
33
+ end
34
+ end
35
+
36
+ it "should raise a name parse error when more than two arguments are given" do
37
+ lambda {
38
+ Latest::Gem.new("rake --pre omg")
39
+ }.must_raise Latest::Gem::NameParseError
40
+ end
41
+
42
+ end
43
+
44
+ describe "#fetch" do
45
+
46
+ before do
47
+ @gem = Latest::Gem.new("rake")
48
+ end
49
+
50
+ it "should return it's url on rubygems" do
51
+ @gem.send(:url).must_equal "https://rubygems.org/api/v1/versions/rake.json"
52
+ end
53
+
54
+ it "should parse it's url into a uri" do
55
+ @gem.send(:uri).must_equal URI.parse("https://rubygems.org/api/v1/versions/rake.json")
56
+ end
57
+
58
+ it "should send a request and return an HTTP response" do
59
+ @gem.send(:send_request).class.ancestors.must_include Net::HTTPResponse
60
+ end
61
+
62
+ it "should fetch and parse response into an array" do
63
+ @gem.fetch
64
+ @gem.versions.must_be_instance_of Array
65
+ end
66
+
67
+ it "should raise GemNotFoundError when gem is not found" do
68
+ @gem.instance_variable_set("@response", Net::HTTPNotFound.new(1.0, 404, "NotFound"))
69
+ lambda {
70
+ @gem.send(:parse_response)
71
+ }.must_raise Latest::Gem::GemNotFoundError
72
+ end
73
+
74
+ it "should throw a RequestError when rubygems responds with an unexpected status" do
75
+ @gem.instance_variable_set("@response", Net::HTTPBadRequest.new(1.0, 400, "BadRequest"))
76
+ lambda {
77
+ @gem.send(:parse_response)
78
+ }.must_raise Latest::Gem::RequestError
79
+ end
80
+
81
+ end
82
+
83
+ describe "#parse_versions" do
84
+
85
+ before do
86
+ @gem = Latest::Gem.new("fake")
87
+ @gem.instance_variable_set("@response", Net::HTTPOK.new(1.0, 200, "OK"))
88
+ @gem.instance_variable_set("@versions", FAKE_VERSIONS)
89
+ end
90
+
91
+ it "should not find latest version if versions are empty" do
92
+ @gem.instance_variable_set("@versions", [])
93
+ @gem.version.must_equal "(not found)"
94
+ end
95
+
96
+ it "should find latest version" do
97
+ @gem.version.must_equal "1.0.1"
98
+ end
99
+
100
+ it "should find latest prerelease version" do
101
+ @gem.instance_variable_set("@pre", true)
102
+ @gem.version.must_equal "1.0.0.rc1"
103
+ end
104
+
105
+ it "should set the total number of downloads when a version is found" do
106
+ @gem.version.must_equal "1.0.1"
107
+ @gem.downloads.must_equal 10000
108
+ end
109
+
110
+ it "should find latest version, change to prerelease, then find the latest version again" do
111
+ @gem.version.must_equal "1.0.1"
112
+ @gem.pre = true
113
+ @gem.version.must_equal "1.0.0.rc1"
114
+ end
115
+
116
+ end
117
+
118
+ end
@@ -0,0 +1,77 @@
1
+ require "latest"
2
+ require "minitest/autorun"
3
+
4
+ describe "Latest" do
5
+
6
+ BIN = File.expand_path("../../bin/latest", __FILE__)
7
+
8
+ STABLE_VERSION_REGEX = /\d+(\.\d+)*/ # 11.2.123
9
+ PRE_VERSION_REGEX = /--pre \d+(\.\d+)*(.[a-z\.?\d]+)+/i # 0.1.23.beta.1
10
+ DOWNLOADS_REGEX = /\(\d+ downloads\)/ # (10 downloads)
11
+
12
+ def assert_version_match(name, string, prerelease=false)
13
+ regex = Regexp.new("#{name} #{prerelease ? PRE_VERSION_REGEX : STABLE_VERSION_REGEX} #{DOWNLOADS_REGEX}")
14
+ string.strip.must_match regex
15
+ end
16
+
17
+ it "should have a version constant" do
18
+ assert Latest.const_defined?(:VERSION)
19
+ end
20
+
21
+ it "should have a latest executable" do
22
+ assert File.exists?(BIN)
23
+ assert File.executable?(BIN)
24
+ end
25
+
26
+ it "should return usage string" do
27
+ Latest.usage.must_equal "Usage: latest [GEM [--pre]] [GEM2 [--pre]] etc"
28
+ end
29
+
30
+ describe "#latest" do
31
+
32
+ def cmd(params)
33
+ `#{BIN} #{params}`.strip
34
+ end
35
+
36
+ it "should return usage when no arguments are given" do
37
+ cmd("").must_equal Latest.usage
38
+ end
39
+
40
+ it "should return version with -v or --version" do
41
+ expected = "Latest v#{Latest::VERSION}"
42
+ cmd("-v").must_equal expected
43
+ cmd("--version").must_equal expected
44
+ end
45
+
46
+ it "should fetch and print multiple gems" do
47
+ out = cmd("rake minitest latest").split("\n")
48
+ assert_version_match "rake", out[0]
49
+ assert_version_match "minitest", out[1]
50
+ assert_version_match "latest", out[2]
51
+ end
52
+
53
+ it "should fetch and print prerelease version" do
54
+ out = cmd("rake --pre")
55
+ assert_version_match "rake", out, true
56
+ end
57
+
58
+ it "should fetch and print mixed normal and prerelease versions" do
59
+ out = cmd("rake --pre shoulda --pre latest").split("\n")
60
+ assert_version_match "rake", out[0], true
61
+ assert_version_match "shoulda", out[1], true
62
+ assert_version_match "latest", out[2]
63
+ end
64
+
65
+ it "should rescue GemNotFoundError and print it's error message" do
66
+ out = cmd("omg-non-existant-gem-2")
67
+ out.must_equal "omg-non-existant-gem-2 [Error] `omg-non-existant-gem-2` could not be found on rubygems.org"
68
+ end
69
+
70
+ it "should rescue standard error and print it's error message" do
71
+ out = cmd("^")
72
+ out.must_equal "^ [Error] bad URI(is not URI?): https://rubygems.org/api/v1/versions/^.json"
73
+ end
74
+
75
+ end
76
+
77
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: latest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-14 00:00:00.000000000Z
12
+ date: 2012-03-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70157104359200 !ruby/object:Gem::Requirement
16
+ requirement: &70325692439400 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>'
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70157104359200
24
+ version_requirements: *70325692439400
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: bundler
27
- requirement: &70157104356100 !ruby/object:Gem::Requirement
27
+ requirement: &70325692438920 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>'
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70157104356100
35
+ version_requirements: *70325692438920
36
36
  description: Latest keeps us up to speed by querying rubygems.org for a gem's most
37
37
  recent version number. Yep, that's all it does.
38
38
  email:
@@ -53,8 +53,8 @@ files:
53
53
  - lib/latest.rb
54
54
  - lib/latest/gem.rb
55
55
  - lib/latest/version.rb
56
- - test/test_gem.rb
57
- - test/test_latest.rb
56
+ - spec/latest/gem_spec.rb
57
+ - spec/latest_spec.rb
58
58
  homepage: https://github.com/citrus/latest
59
59
  licenses: []
60
60
  post_install_message:
@@ -73,13 +73,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
73
  - - ! '>='
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
+ segments:
77
+ - 0
78
+ hash: -2568820004086336079
76
79
  requirements: []
77
80
  rubyforge_project: latest
78
- rubygems_version: 1.8.10
81
+ rubygems_version: 1.8.11
79
82
  signing_key:
80
83
  specification_version: 3
81
- summary: Latest keeps us up to speed by querying rubygems.org for a gem's most recent
82
- version number. Yep, that's all it does.
84
+ summary: Latest queries rubygems.org for the latest version of a gem.
83
85
  test_files:
84
- - test/test_gem.rb
85
- - test/test_latest.rb
86
+ - spec/latest/gem_spec.rb
87
+ - spec/latest_spec.rb
data/test/test_gem.rb DELETED
@@ -1,18 +0,0 @@
1
- require "minitest/autorun"
2
- require "latest"
3
-
4
- class TestGem < MiniTest::Unit::TestCase
5
-
6
- def test_should_fetch_on_initialize
7
- gem = ::Latest::Gem.new("rake")
8
- assert_equal Array, gem.versions.class
9
- assert_equal "rake", gem.name
10
- end
11
-
12
- def test_raise_error_when_gem_is_not_found
13
- assert_raises Latest::GemNotFoundError do
14
- ::Latest::Gem.new("some-gem-that-probably-wont-exist-2")
15
- end
16
- end
17
-
18
- end
data/test/test_latest.rb DELETED
@@ -1,56 +0,0 @@
1
- require "minitest/autorun"
2
- require "latest"
3
-
4
- class TestLatest < MiniTest::Unit::TestCase
5
-
6
- BIN = File.expand_path("../../bin/latest", __FILE__)
7
-
8
- STABLE_VERSION_REGEX = /\d+(\.\d+)*$/ # 11.2.123
9
- PRERELEASE_VERSION_REGEX = /--pre \d+(\.\d+)*(.[a-z\.\d]+)+$/i # 0.1.23.beta.1
10
-
11
- def assert_version_match(name, string, prerelease=false)
12
- assert_match Regexp.new("#{name} #{prerelease ? PRERELEASE_VERSION_REGEX : STABLE_VERSION_REGEX}"), string.strip
13
- end
14
-
15
- def test_has_version
16
- assert_equal String, Latest::VERSION.class
17
- end
18
-
19
- def test_executable_exists
20
- assert File.exists?(BIN)
21
- end
22
-
23
- def test_executable_is_executable
24
- assert File.executable?(BIN)
25
- end
26
-
27
- def test_executable_returns_version
28
- out = `#{BIN} -v`
29
- assert_match "Latest v#{Latest::VERSION}", out
30
- end
31
-
32
- def test_executable_fetches_and_prints_version
33
- out = `#{BIN} rake`
34
- assert_version_match "rake", out
35
- end
36
-
37
- def test_executable_fetches_and_prints_multiple_versions
38
- out = `#{BIN} rake rails spree`.split("\n")
39
- assert_version_match "rake", out[0]
40
- assert_version_match "rails", out[1]
41
- assert_version_match "spree", out[2]
42
- end
43
-
44
- def test_executable_fetches_and_prints_prerelease_version
45
- out = `#{BIN} rake --pre`
46
- assert_version_match "rake", out, true
47
- end
48
-
49
- def test_executable_fetches_and_prints_multiple_prerelease_versions
50
- out = `#{BIN} rake --pre rails --pre spree`.split("\n")
51
- assert_version_match "rake", out[0], true
52
- assert_version_match "rails", out[1], true
53
- assert_version_match "spree", out[2]
54
- end
55
-
56
- end