puppet_forge 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +22 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +13 -0
  4. data/README.md +172 -0
  5. data/Rakefile +2 -0
  6. data/lib/her/lazy_accessors.rb +140 -0
  7. data/lib/her/lazy_relations.rb +86 -0
  8. data/lib/puppet_forge.rb +19 -0
  9. data/lib/puppet_forge/middleware/json_for_her.rb +37 -0
  10. data/lib/puppet_forge/v3.rb +10 -0
  11. data/lib/puppet_forge/v3/base.rb +98 -0
  12. data/lib/puppet_forge/v3/base/paginated_collection.rb +73 -0
  13. data/lib/puppet_forge/v3/module.rb +15 -0
  14. data/lib/puppet_forge/v3/release.rb +35 -0
  15. data/lib/puppet_forge/v3/user.rb +21 -0
  16. data/lib/puppet_forge/version.rb +3 -0
  17. data/puppet_forge.gemspec +31 -0
  18. data/spec/fixtures/v3/files/puppetlabs-apache-0.0.1.tar.gz.headers +14 -0
  19. data/spec/fixtures/v3/files/puppetlabs-apache-0.0.1.tar.gz.json +0 -0
  20. data/spec/fixtures/v3/modules.headers +14 -0
  21. data/spec/fixtures/v3/modules.json +4197 -0
  22. data/spec/fixtures/v3/modules/puppetlabs-apache.headers +14 -0
  23. data/spec/fixtures/v3/modules/puppetlabs-apache.json +390 -0
  24. data/spec/fixtures/v3/modules?owner=puppetlabs.headers +14 -0
  25. data/spec/fixtures/v3/modules?owner=puppetlabs.json +4179 -0
  26. data/spec/fixtures/v3/modules?query=apache.headers +14 -0
  27. data/spec/fixtures/v3/modules?query=apache.json +3151 -0
  28. data/spec/fixtures/v3/releases.headers +14 -0
  29. data/spec/fixtures/v3/releases.json +3072 -0
  30. data/spec/fixtures/v3/releases/puppetlabs-apache-0.0.1.headers +14 -0
  31. data/spec/fixtures/v3/releases/puppetlabs-apache-0.0.1.json +93 -0
  32. data/spec/fixtures/v3/releases/puppetlabs-apache-0.0.2.headers +14 -0
  33. data/spec/fixtures/v3/releases/puppetlabs-apache-0.0.2.json +93 -0
  34. data/spec/fixtures/v3/releases/puppetlabs-apache-0.0.3.headers +14 -0
  35. data/spec/fixtures/v3/releases/puppetlabs-apache-0.0.3.json +93 -0
  36. data/spec/fixtures/v3/releases/puppetlabs-apache-0.0.4.headers +14 -0
  37. data/spec/fixtures/v3/releases/puppetlabs-apache-0.0.4.json +126 -0
  38. data/spec/fixtures/v3/releases/puppetlabs-apache-0.1.1.headers +14 -0
  39. data/spec/fixtures/v3/releases/puppetlabs-apache-0.1.1.json +140 -0
  40. data/spec/fixtures/v3/releases?module=puppetlabs-apache.headers +14 -0
  41. data/spec/fixtures/v3/releases?module=puppetlabs-apache.json +3287 -0
  42. data/spec/fixtures/v3/users/puppetlabs.headers +14 -0
  43. data/spec/fixtures/v3/users/puppetlabs.json +10 -0
  44. data/spec/spec_helper.rb +60 -0
  45. data/spec/unit/forge/v3/base/paginated_collection_spec.rb +88 -0
  46. data/spec/unit/forge/v3/module_spec.rb +118 -0
  47. data/spec/unit/forge/v3/release_spec.rb +112 -0
  48. data/spec/unit/forge/v3/user_spec.rb +50 -0
  49. data/spec/unit/her/lazy_accessors_spec.rb +142 -0
  50. data/spec/unit/her/lazy_relations_spec.rb +309 -0
  51. metadata +261 -0
@@ -0,0 +1,14 @@
1
+ HTTP/1.1 200 OK
2
+ Server: nginx
3
+ Date: Mon, 06 Jan 2014 22:42:07 GMT
4
+ Content-Type: application/json;charset=utf-8
5
+ Content-Length: 285
6
+ Connection: keep-alive
7
+ Status: 200 OK
8
+ X-Frame-Options: sameorigin
9
+ X-XSS-Protection: 1; mode=block
10
+ Cache-Control: public, must-revalidate
11
+ Last-Modified: Mon, 12 Nov 2012 14:51:00 GMT
12
+ X-Node: forgeapi02
13
+ X-Revision: 49f66e061b0021c081fb58e754898cd928f61494
14
+
@@ -0,0 +1,10 @@
1
+ {
2
+ "uri": "/v3/users/puppetlabs",
3
+ "gravatar_id": "fdd009b7c1ec96e088b389f773e87aec",
4
+ "username": "puppetlabs",
5
+ "display_name": "Puppet Labs",
6
+ "release_count": 422,
7
+ "module_count": 81,
8
+ "created_at": "2010-05-19 05:46:26 -0700",
9
+ "updated_at": "2012-11-12 06:51:00 -0800"
10
+ }
@@ -0,0 +1,60 @@
1
+ PROJECT_ROOT = File.join(File.dirname(__FILE__), '..')
2
+
3
+ if ENV['COVERAGE']
4
+ require 'simplecov'
5
+ SimpleCov.start do
6
+ add_filter "/spec/"
7
+ end
8
+
9
+ end
10
+
11
+ require 'puppet_forge'
12
+
13
+ module StubbingHer
14
+ def stub_api_for(klass)
15
+ klass.use_api begin
16
+ Her::API.new :url => "http://api.example.com" do |c|
17
+ c.use PuppetForge::Middleware::JSONForHer
18
+ c.adapter(:test) { |s| yield(s) }
19
+ end
20
+ end
21
+ end
22
+
23
+ def stub_fixture(api, method, path)
24
+ api.send(method, path) do |env|
25
+ load_fixture([ env[:url].path, env[:url].query ].compact.join('?'))
26
+ end
27
+ end
28
+
29
+ def load_fixture(path)
30
+ [ 404 ].tap do |response|
31
+ local = File.join(PROJECT_ROOT, 'spec', 'fixtures', path.to_s)
32
+
33
+ if File.exists?("#{local}.headers") && File.exists?("#{local}.json")
34
+ File.open("#{local}.headers") do |file|
35
+ response[0] = file.readline[/\d{3}/].to_i
36
+ response[1] = headers = {}
37
+ file.read.scan(/^([^:]+?):(.*)/) do |key, val|
38
+ headers[key] = val.strip
39
+ end
40
+ end
41
+
42
+ response << File.read("#{local}.json")
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ RSpec.configure do |config|
49
+ config.treat_symbols_as_metadata_keys_with_true_values = true
50
+ config.run_all_when_everything_filtered = true
51
+ config.filter_run :focus
52
+
53
+ config.include StubbingHer
54
+
55
+ # Run specs in random order to surface order dependencies. If you find an
56
+ # order dependency and want to debug it, you can fix the order by providing
57
+ # the seed, which is printed after each run.
58
+ # --seed 1234
59
+ config.order = 'random'
60
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ describe PuppetForge::V3::Base::PaginatedCollection do
4
+ let(:klass) do
5
+ Class.new do
6
+ def self.get_collection(url)
7
+ data = {
8
+ '/v3/collection' => [ :A, :B, :C ],
9
+ '/v3/collection?page=2' => [ :D, :E, :F ],
10
+ '/v3/collection?page=3' => [ :G, :H ],
11
+ }
12
+
13
+ meta = {
14
+ '/v3/collection' => {
15
+ :limit => 3,
16
+ :offset => 0,
17
+ :first => '/v3/collection',
18
+ :previous => nil,
19
+ :current => '/v3/collection',
20
+ :next => '/v3/collection?page=2',
21
+ :total => 8,
22
+ },
23
+ '/v3/collection?page=2' => {
24
+ :limit => 3,
25
+ :offset => 0,
26
+ :first => '/v3/collection',
27
+ :previous => '/v3/collection',
28
+ :current => '/v3/collection?page=2',
29
+ :next => '/v3/collection?page=3',
30
+ :total => 8,
31
+ },
32
+ '/v3/collection?page=3' => {
33
+ :limit => 3,
34
+ :offset => 0,
35
+ :first => '/v3/collection',
36
+ :previous => '/v3/collection?page=2',
37
+ :current => '/v3/collection?page=3',
38
+ :next => nil,
39
+ :total => 8,
40
+ },
41
+ }
42
+
43
+ PuppetForge::V3::Base::PaginatedCollection.new(self, data[url], meta[url], {})
44
+ end
45
+ end
46
+ end
47
+
48
+ subject { klass.get_collection('/v3/collection') }
49
+
50
+ it 'maps to a single page of the collection' do
51
+ expect(subject.to_a).to eql([ :A, :B, :C ])
52
+ end
53
+
54
+ it 'knows the size of the entire collection' do
55
+ expect(subject.total).to be 8
56
+ end
57
+
58
+ it 'contains only a subset of the entire collection' do
59
+ expect(subject.size).to be 3
60
+ end
61
+
62
+ it 'enables page navigation' do
63
+ expect(subject.next).to_not be_empty
64
+ expect(subject.next.to_a).to_not eql(subject.to_a)
65
+ expect(subject.next.previous.to_a).to eql(subject.to_a)
66
+ end
67
+
68
+ it 'exposes the pagination metadata' do
69
+ expect(subject.metadata[:limit]).to be subject.size
70
+ end
71
+
72
+ it 'exposes previous_url and next_url' do
73
+ expected = subject.next_url
74
+ expect(subject.next.next.previous_url).to eql(expected)
75
+ end
76
+
77
+ describe '#unpaginated' do
78
+ it 'provides an iterator over the entire collection' do
79
+ expected = [ :A, :B, :C, :D, :E, :F, :G, :H ]
80
+ expect(subject.unpaginated.to_a).to eql(expected)
81
+ end
82
+
83
+ it "provides a full iterator regardless of which page it's started on" do
84
+ expected = [ :A, :B, :C, :D, :E, :F, :G, :H ]
85
+ expect(subject.next.next.unpaginated.to_a).to eql(expected)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe PuppetForge::V3::Module do
4
+ before do
5
+ stub_api_for(PuppetForge::V3::Module) do |api|
6
+ stub_fixture(api, :get, '/v3/modules/puppetlabs-apache')
7
+ stub_fixture(api, :get, '/v3/modules/absent-apache')
8
+ end
9
+ end
10
+
11
+ describe '::find' do
12
+ let(:mod) { PuppetForge::V3::Module.find('puppetlabs-apache') }
13
+ let(:missing_mod) { PuppetForge::V3::Module.find('absent-apache') }
14
+
15
+ it 'can find modules that exist' do
16
+ mod.name.should == 'apache'
17
+ end
18
+
19
+ it 'returns nil for non-existent modules' do
20
+ missing_mod.should be_nil
21
+ end
22
+ end
23
+
24
+ describe '#owner' do
25
+ let(:mod) { PuppetForge::V3::Module.find('puppetlabs-apache') }
26
+
27
+ before do
28
+ stub_api_for(PuppetForge::V3::User) do |api|
29
+ stub_fixture(api, :get, '/v3/users/puppetlabs')
30
+ end
31
+ end
32
+
33
+ it 'exposes the related module as a property' do
34
+ expect(mod.owner).to_not be nil
35
+ end
36
+
37
+ it 'grants access to module attributes without an API call' do
38
+ PuppetForge::V3::User.should_not_receive(:request)
39
+ expect(mod.owner.username).to eql('puppetlabs')
40
+ end
41
+
42
+ it 'transparently makes API calls for other attributes' do
43
+ PuppetForge::V3::User.should_receive(:request).once.and_call_original
44
+ expect(mod.owner.created_at).to_not be nil
45
+ end
46
+ end
47
+
48
+ describe '#current_release' do
49
+ let(:mod) { PuppetForge::V3::Module.find('puppetlabs-apache') }
50
+
51
+ it 'exposes the current_release as a property' do
52
+ expect(mod.current_release).to_not be nil
53
+ end
54
+
55
+ it 'grants access to release attributes without an API call' do
56
+ PuppetForge::V3::Release.should_not_receive(:request)
57
+ expect(mod.current_release.version).to_not be nil
58
+ end
59
+
60
+ it 'transparently makes API calls for other attributes' do
61
+ stub_api_for(PuppetForge::V3::Release) do |api|
62
+ api.get(mod.current_release.uri) do
63
+ load_fixture('/v3/releases/puppetlabs-apache-0.0.1')
64
+ end
65
+ end
66
+
67
+ mod.attributes[:current_release].delete :created_at
68
+ expect(mod.current_release.created_at).to_not be nil
69
+ end
70
+ end
71
+
72
+ describe '#releases' do
73
+ let(:mod) { PuppetForge::V3::Module.find('puppetlabs-apache') }
74
+
75
+ before do
76
+ stub_api_for(PuppetForge::V3::Release) do |api|
77
+ stub_fixture(api, :get, '/v3/releases/puppetlabs-apache-0.0.1')
78
+ stub_fixture(api, :get, '/v3/releases/puppetlabs-apache-0.0.2')
79
+ stub_fixture(api, :get, '/v3/releases/puppetlabs-apache-0.0.3')
80
+ stub_fixture(api, :get, '/v3/releases/puppetlabs-apache-0.0.4')
81
+ stub_fixture(api, :get, '/v3/releases/puppetlabs-apache-0.1.1')
82
+ stub_fixture(api, :get, '/v3/releases?module=puppetlabs-apache')
83
+ end
84
+ end
85
+
86
+ it 'exposes the related releases as a property' do
87
+ expect(mod.releases).to be_an Array
88
+ end
89
+
90
+ it 'knows the size of the collection' do
91
+ expect(mod.releases).to_not be_empty
92
+ end
93
+
94
+ it 'grants access to release attributes without an API call' do
95
+ PuppetForge::V3::Release.should_not_receive(:request)
96
+ expect(mod.releases.map(&:version)).to_not include nil
97
+ end
98
+
99
+ it 'transparently makes API calls for other attributes' do
100
+ versions = %w[ 0.0.1 0.0.2 0.0.3 0.0.4 0.1.1 ]
101
+ releases = mod.releases.select { |x| versions.include? x.version }
102
+
103
+ PuppetForge::V3::Release.should_receive(:request) \
104
+ .exactly(5).times \
105
+ .and_call_original
106
+
107
+ expect(releases.map(&:created_at)).to_not include nil
108
+ end
109
+ end
110
+
111
+ describe 'instance properies' do
112
+ let(:mod) { PuppetForge::V3::Module.find('puppetlabs-apache') }
113
+
114
+ example 'are easily accessible' do
115
+ expect(mod.created_at).to_not be nil
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+
4
+ describe PuppetForge::V3::Release do
5
+ before do
6
+ stub_api_for(PuppetForge::V3::Release) do |api|
7
+ stub_fixture(api, :get, '/v3/releases/puppetlabs-apache-0.0.1')
8
+ stub_fixture(api, :get, '/v3/releases/absent-apache-0.0.1')
9
+ end
10
+ end
11
+
12
+ describe '::find' do
13
+ let(:release) { PuppetForge::V3::Release.find('puppetlabs-apache-0.0.1') }
14
+ let(:missing_release) { PuppetForge::V3::Release.find('absent-apache-0.0.1') }
15
+
16
+ it 'can find releases that exist' do
17
+ expect(release.version).to eql('0.0.1')
18
+ end
19
+
20
+ it 'returns nil for non-existent releases' do
21
+ expect(missing_release).to be nil
22
+ end
23
+ end
24
+
25
+ describe '#module' do
26
+ let(:release) { PuppetForge::V3::Release.find('puppetlabs-apache-0.0.1') }
27
+
28
+ before do
29
+ stub_api_for(PuppetForge::V3::Module) do |api|
30
+ stub_fixture(api, :get, '/v3/modules/puppetlabs-apache')
31
+ end
32
+ end
33
+
34
+ it 'exposes the related module as a property' do
35
+ expect(release.module).to_not be nil
36
+ end
37
+
38
+ it 'grants access to module attributes without an API call' do
39
+ PuppetForge::V3::Module.should_not_receive(:request)
40
+ expect(release.module.name).to eql('apache')
41
+ end
42
+
43
+ it 'transparently makes API calls for other attributes' do
44
+ PuppetForge::V3::Module.should_receive(:request).once.and_call_original
45
+ expect(release.module.created_at).to_not be nil
46
+ end
47
+ end
48
+
49
+ describe '#download_url' do
50
+ let(:release) { PuppetForge::V3::Release.find('puppetlabs-apache-0.0.1') }
51
+
52
+ before do
53
+ stub_api_for(PuppetForge::V3::Release) do |api|
54
+ stub_fixture(api, :get, '/v3/releases/puppetlabs-apache-0.0.1')
55
+ end
56
+ end
57
+
58
+ it 'handles an API response that does not include a scheme and host' do
59
+ release.file_uri = '/v3/files/puppetlabs-apache-0.0.1.tar.gz'
60
+ expect(release.download_url).to eql(PuppetForge.host + '/v3/files/puppetlabs-apache-0.0.1.tar.gz')
61
+ end
62
+
63
+ it 'handles an API response that includes a scheme and host' do
64
+ release.file_uri = 'https://example.com/v3/files/puppetlabs-apache-0.0.1.tar.gz'
65
+ expect(release.download_url).to eql('https://example.com/v3/files/puppetlabs-apache-0.0.1.tar.gz')
66
+ end
67
+ end
68
+
69
+ describe '#download' do
70
+ let(:release) { PuppetForge::V3::Release.find('puppetlabs-apache-0.0.1') }
71
+ let(:tarball) { "#{PROJECT_ROOT}/spec/tmp/module.tgz" }
72
+
73
+ before { FileUtils.rm tarball rescue nil }
74
+ after { FileUtils.rm tarball rescue nil }
75
+ before do
76
+ stub_api_for(PuppetForge::V3::Release) do |api|
77
+ stub_fixture(api, :get, '/v3/releases/puppetlabs-apache-0.0.1')
78
+ stub_fixture(api, :get, '/v3/files/puppetlabs-apache-0.0.1.tar.gz')
79
+ end
80
+ end
81
+
82
+ it 'downloads the file to the specified location' do
83
+ expect(File.exist?(tarball)).to be false
84
+ release.download(tarball)
85
+ expect(File.exist?(tarball)).to be true
86
+ end
87
+ end
88
+
89
+ describe '#metadata' do
90
+ let(:release) { PuppetForge::V3::Release.find('puppetlabs-apache-0.0.1') }
91
+
92
+ before do
93
+ stub_api_for(PuppetForge::V3::Module) do |api|
94
+ stub_fixture(api, :get, '/v3/modules/puppetlabs-apache')
95
+ end
96
+ end
97
+
98
+ it 'is lazy and repeatable' do
99
+ 3.times do
100
+ expect(release.module.releases.last.metadata).to_not be_nil
101
+ end
102
+ end
103
+ end
104
+
105
+ describe 'instance properies' do
106
+ let(:release) { PuppetForge::V3::Release.find('puppetlabs-apache-0.0.1') }
107
+
108
+ example 'are easily accessible' do
109
+ expect(release.created_at).to_not be nil
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe PuppetForge::V3::User do
4
+ before do
5
+ stub_api_for(PuppetForge::V3::User) do |api|
6
+ stub_fixture(api, :get, '/v3/users/puppetlabs')
7
+ stub_fixture(api, :get, '/v3/users/absent')
8
+ end
9
+ end
10
+
11
+ describe '::find' do
12
+ let(:user) { PuppetForge::V3::User.find('puppetlabs') }
13
+ let(:missing_user) { PuppetForge::V3::User.find('absent') }
14
+
15
+ it 'can find users that exist' do
16
+ user.username.should == 'puppetlabs'
17
+ end
18
+
19
+ it 'returns nil for non-existent users' do
20
+ missing_user.should be_nil
21
+ end
22
+ end
23
+
24
+ describe '#modules' do
25
+ before do
26
+ stub_api_for(PuppetForge::V3::Module) do |api|
27
+ stub_fixture(api, :get, '/v3/modules?owner=puppetlabs')
28
+ end
29
+ end
30
+
31
+ let(:user) { PuppetForge::V3::User.find('puppetlabs') }
32
+
33
+ it 'should return a PaginatedCollection' do
34
+ expect(user.modules).to be_a PuppetForge::V3::Base::PaginatedCollection
35
+ end
36
+
37
+ it 'should only return modules for the current user' do
38
+ module_owners = user.modules.map(&:owner)
39
+ expect(module_owners.group_by(&:username).keys).to eql(['puppetlabs'])
40
+ end
41
+ end
42
+
43
+ describe 'instance properies' do
44
+ let(:user) { PuppetForge::V3::User.find('puppetlabs') }
45
+
46
+ example 'are easily accessible' do
47
+ expect(user.created_at).to_not be nil
48
+ end
49
+ end
50
+ end