latest 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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