metal_archives 0.8.0 → 1.0.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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +59 -7
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +14 -0
  5. data/.travis.yml +11 -0
  6. data/Gemfile +2 -0
  7. data/{LICENSE → LICENSE.md} +0 -0
  8. data/README.md +77 -9
  9. data/Rakefile +5 -3
  10. data/lib/metal_archives.rb +8 -0
  11. data/lib/metal_archives/configuration.rb +28 -7
  12. data/lib/metal_archives/error.rb +37 -30
  13. data/lib/metal_archives/http_client.rb +21 -42
  14. data/lib/metal_archives/middleware/headers.rb +38 -0
  15. data/lib/metal_archives/middleware/rewrite_endpoint.rb +38 -0
  16. data/lib/metal_archives/models/artist.rb +51 -65
  17. data/lib/metal_archives/models/band.rb +41 -39
  18. data/lib/metal_archives/models/base_model.rb +88 -59
  19. data/lib/metal_archives/models/label.rb +7 -6
  20. data/lib/metal_archives/parsers/artist.rb +110 -99
  21. data/lib/metal_archives/parsers/band.rb +168 -156
  22. data/lib/metal_archives/parsers/label.rb +54 -52
  23. data/lib/metal_archives/parsers/parser.rb +73 -71
  24. data/lib/metal_archives/utils/collection.rb +7 -1
  25. data/lib/metal_archives/utils/lru_cache.rb +11 -4
  26. data/lib/metal_archives/utils/nil_date.rb +54 -0
  27. data/lib/metal_archives/utils/range.rb +16 -8
  28. data/lib/metal_archives/version.rb +3 -1
  29. data/metal_archives.gemspec +21 -11
  30. data/spec/configuration_spec.rb +101 -0
  31. data/spec/factories/artist_factory.rb +37 -0
  32. data/spec/factories/band_factory.rb +60 -0
  33. data/spec/factories/nil_date_factory.rb +9 -0
  34. data/spec/factories/range_factory.rb +8 -0
  35. data/spec/models/artist_spec.rb +142 -0
  36. data/spec/models/band_spec.rb +179 -0
  37. data/spec/models/base_model_spec.rb +217 -0
  38. data/spec/parser_spec.rb +19 -0
  39. data/spec/spec_helper.rb +111 -0
  40. data/spec/support/factory_girl.rb +5 -0
  41. data/spec/support/metal_archives.rb +26 -0
  42. data/spec/utils/collection_spec.rb +72 -0
  43. data/spec/utils/lru_cache_spec.rb +53 -0
  44. data/spec/utils/nil_date_spec.rb +98 -0
  45. data/spec/utils/range_spec.rb +62 -0
  46. metadata +142 -57
  47. data/test/base_model_test.rb +0 -111
  48. data/test/configuration_test.rb +0 -57
  49. data/test/parser_test.rb +0 -37
  50. data/test/property/artist_property_test.rb +0 -43
  51. data/test/property/band_property_test.rb +0 -94
  52. data/test/query/artist_query_test.rb +0 -109
  53. data/test/query/band_query_test.rb +0 -152
  54. data/test/test_helper.rb +0 -25
  55. data/test/utils/collection_test.rb +0 -51
  56. data/test/utils/lru_cache_test.rb +0 -22
  57. data/test/utils/range_test.rb +0 -42
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryGirl.define do
4
+ factory :artist, :class => MetalArchives::Artist do
5
+ id { Faker::Number.number [1, 2, 3, 4].sample }
6
+ name { Faker::Name.name }
7
+ gender { %i[male female].sample }
8
+ biography { Faker::Lorem.words(200).join ' ' }
9
+ trivia { Faker::Lorem.words(200).join ' ' }
10
+
11
+ country { ISO3166::Country[Faker::Address.country_code] }
12
+ location { Faker::Address.city }
13
+
14
+ date_of_birth { Faker::Date.birthday 18, 65 }
15
+
16
+ links do
17
+ 3.times.collect do
18
+ {
19
+ :url => Faker::Internet.url,
20
+ :type => %i[official unofficial unlisted_bands].sample,
21
+ :title => Faker::Lorem.words(4).join(' ')
22
+ }
23
+ end
24
+ end
25
+
26
+ trait :has_died do
27
+ date_of_death { Faker::Date.between date_of_birth, Date.today }
28
+ cause_of_death { %w(Suicide N/A Accident Cancer Illness Murder).sample }
29
+ end
30
+
31
+ trait :with_aliases do
32
+ aliases do
33
+ 3.times.collect { Faker::Name.name }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryGirl.define do
4
+ factory :band, :class => MetalArchives::Band do
5
+ id { Faker::Number.number [1, 2, 3, 4].sample }
6
+ name { Faker::Name.name }
7
+ status { %i[active split_up on_hold unknown changed_name disputed].sample }
8
+
9
+ comment { Faker::Lorem.words(200).join ' ' }
10
+
11
+ country { ISO3166::Country[Faker::Address.country_code] }
12
+ location { Faker::Address.city }
13
+
14
+ date_formed { Faker::Date.birthday 0, 50 }
15
+ date_active { build_list :range }
16
+
17
+ label { [build(:label), nil].sample }
18
+ independent { label.nil? }
19
+
20
+ logo { Faker::Internet.url }
21
+ photo { Faker::Internet.url }
22
+
23
+ genres do
24
+ 3.times.collect do
25
+ "#{%w(Black Death Doom Power Progressive Speed Thrash).sample} Metal"
26
+ end
27
+ end
28
+
29
+ lyrical_themes do
30
+ 3.times.collect do
31
+ ['Fantasy', 'Epic battles', 'Tales', 'Myths', 'Legends', 'Feelings', 'Life', 'Eden', 'Glory', 'the Four Elements', 'Metal'].sample
32
+ end
33
+ end
34
+
35
+ similar do
36
+ 4.times.collect do
37
+ {
38
+ :band => build(:band),
39
+ :score => Faker::Number.between(1, 100)
40
+ }
41
+ end
42
+ end
43
+
44
+ links do
45
+ 3.times.collect do
46
+ {
47
+ :url => Faker::Internet.url,
48
+ :type => %i[official unofficial unlisted_bands].sample,
49
+ :title => Faker::Lorem.words(4).join(' ')
50
+ }
51
+ end
52
+ end
53
+
54
+ trait :with_aliases do
55
+ aliases do
56
+ 3.times.collect { Faker::Name.name }
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryGirl.define do
4
+ factory :nil_date, :class => MetalArchives::NilDate do
5
+ year { Faker::Date.backward(99).year }
6
+ month { Faker::Date.backward(99).month }
7
+ day { Faker::Date.backward(99).day }
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryGirl.define do
4
+ factory :range, :class => MetalArchives::Range do
5
+ send(:begin) { Faker::Date.birthday 0, 50 }
6
+ send(:end) { Faker::Date.birthday }
7
+ end
8
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MetalArchives::Artist do
4
+ describe 'properties' do
5
+ it 'Alberto Rionda has properties' do
6
+ artist = MetalArchives::Artist.find 60908
7
+
8
+ expect(artist).to be_instance_of MetalArchives::Artist
9
+ expect(artist.id).to eq 60908
10
+ expect(artist.name).to eq 'Alberto Rionda'
11
+ expect(artist.aliases).to be_empty
12
+ expect(artist.country).to eq ISO3166::Country['ES']
13
+ expect(artist.location).to eq 'Oviedo, Asturias'
14
+ expect(artist.date_of_birth).to eq Date.new(1972, 9, 2)
15
+ expect(artist.gender).to eq :male
16
+ expect(artist.biography).to match 'Avalanch'
17
+ expect(artist.trivia).to match 'Sanctuarium Estudios'
18
+ expect(artist.photo).to be_instance_of URI::HTTPS
19
+ expect(artist.photo.path).to eq '/images/6/0/9/0/60908_artist.jpg'
20
+ end
21
+
22
+ it 'Lemmy Kilmister has properties' do
23
+ artist = MetalArchives::Artist.find 260
24
+
25
+ expect(artist).to be_instance_of MetalArchives::Artist
26
+ expect(artist.name).to eq 'Ian Fraser Kilmister'
27
+ expect(artist.aliases).to include 'Lemmy Kilmister'
28
+ expect(artist.date_of_death).to eq Date.new(2015, 12, 28)
29
+ expect(artist.links.length).to eq 5
30
+ expect(artist.links.count { |l| l[:type] == :official }).to eq 1
31
+ expect(artist.links.count { |l| l[:type] == :unofficial }).to eq 2
32
+ expect(artist.links.count { |l| l[:type] == :unlisted_bands }).to eq 2
33
+ expect(artist.links.select { |l| l[:type] == :official }.first[:url]).to eq 'https://www.facebook.com/OfficialLemmy'
34
+ expect(artist.links.select { |l| l[:type] == :official }.first[:title]).to eq 'Facebook'
35
+ end
36
+
37
+ it 'maps query parameters' do
38
+ expect(MetalArchives::Parsers::Artist.map_params(:name => 'name')[:query]).to eq 'name'
39
+ end
40
+
41
+ it 'rewrites URIs' do
42
+ old_endpoint = MetalArchives.config.endpoint
43
+ MetalArchives.config.endpoint = 'https://foo.bar/'
44
+
45
+ artist = MetalArchives::Artist.find! 60908
46
+
47
+ expect(artist.photo.scheme).to eq 'https'
48
+ expect(artist.photo.host).to eq 'foo.bar'
49
+
50
+ MetalArchives.config.endpoint = old_endpoint
51
+ end
52
+ end
53
+
54
+ describe 'methods' do
55
+ describe 'find' do
56
+ it 'finds an artist' do
57
+ artist = MetalArchives::Artist.find 60908
58
+
59
+ expect(artist).to be_instance_of MetalArchives::Artist
60
+ expect(artist.name).to eq 'Alberto Rionda'
61
+ end
62
+
63
+ it 'lazily loads' do
64
+ artist = MetalArchives::Artist.find -1
65
+
66
+ expect(artist).to be_instance_of MetalArchives::Artist
67
+ end
68
+ end
69
+
70
+ describe 'find!' do
71
+ it 'finds an artist' do
72
+ artist = MetalArchives::Artist.find! 60908
73
+
74
+ expect(artist).to be_instance_of MetalArchives::Artist
75
+ expect(artist.name).to eq 'Alberto Rionda'
76
+ end
77
+
78
+ it 'raises on invalid id' do
79
+ expect(-> { MetalArchives::Artist.find! -1 }).to raise_error MetalArchives::Errors::APIError
80
+ expect(-> { MetalArchives::Artist.find! 0 }).to raise_error MetalArchives::Errors::InvalidIDError
81
+ expect(-> { MetalArchives::Artist.find! nil }).to raise_error MetalArchives::Errors::InvalidIDError
82
+ end
83
+ end
84
+
85
+ describe 'find_by' do
86
+ it 'finds an artist' do
87
+ artist = MetalArchives::Artist.find_by :name => 'Alberto Rionda'
88
+
89
+ expect(artist).to be_instance_of MetalArchives::Artist
90
+ expect(artist.id).to eq 60908
91
+ end
92
+
93
+ it 'returns nil on invalid id' do
94
+ artist = MetalArchives::Artist.find_by :name => 'SomeNonExistantName'
95
+
96
+ expect(artist).to be_nil
97
+ end
98
+ end
99
+
100
+ describe 'find_by!' do
101
+ it 'finds an artist' do
102
+ artist = MetalArchives::Artist.find_by! :name => 'Alberto Rionda'
103
+
104
+ expect(artist).to be_instance_of MetalArchives::Artist
105
+ expect(artist.id).to eq 60908
106
+ end
107
+
108
+ it 'returns nil on invalid id' do
109
+ artist = MetalArchives::Artist.find_by! :name => 'SomeNonExistantName'
110
+
111
+ expect(artist).to be_nil
112
+ end
113
+ end
114
+
115
+ describe 'search' do
116
+ it 'returns a collection' do
117
+ collection = MetalArchives::Artist.search 'Alberto Rionda'
118
+
119
+ expect(collection).to be_instance_of MetalArchives::Collection
120
+ expect(collection.first).to be_instance_of MetalArchives::Artist
121
+ end
122
+
123
+ it 'returns a collection' do
124
+ expect(MetalArchives::Artist.search('Alberto Rionda').count).to eq 1
125
+ expect(MetalArchives::Artist.search('Name').count).to eq 10
126
+ expect(MetalArchives::Artist.search('SomeNonExistantName').count).to eq 0
127
+ expect(MetalArchives::Artist.search 'SomeNonExistantName').to be_empty
128
+ expect(MetalArchives::Artist.search('Filip').count).to be > 200
129
+ end
130
+
131
+ it 'returns an empty collection' do
132
+ expect(MetalArchives::Artist.search 'SomeNoneExistantName').to be_empty
133
+ end
134
+ end
135
+
136
+ describe 'all' do
137
+ it 'returns a collection' do
138
+ expect(MetalArchives::Artist.all).to be_instance_of MetalArchives::Collection
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MetalArchives::Band do
4
+ describe 'properties' do
5
+ it 'Pathfinder has properties' do
6
+ band = MetalArchives::Band.find 122302
7
+
8
+ expect(band).to be_instance_of MetalArchives::Band
9
+ expect(band.name).to eq 'Pathfinder'
10
+ expect(band.aliases).to be_empty
11
+ expect(band.country).to eq ISO3166::Country['PL']
12
+ expect(band.location).to eq 'Poznań'
13
+ expect(band.date_formed).to eq Date.new(2006)
14
+ expect(band.date_active).to eq [MetalArchives::Range.new(Date.new(2006), nil)]
15
+ expect(band.status).to eq :active
16
+ expect(band.genres).to eq ['Symphonic Power']
17
+ expect(band.lyrical_themes.sort).to eq ['Fantasy', 'Battles', 'Glory', 'The Four Elements', 'Metal'].sort
18
+ expect(band.comment).to match 'Pathfinder was founded by'
19
+ expect(band.logo).to be_instance_of URI::HTTPS
20
+ expect(band.logo.path).to eq '/images/1/2/2/3/122302_logo.jpg'
21
+ expect(band.photo).to be_instance_of URI::HTTPS
22
+ expect(band.photo.path).to eq '/images/1/2/2/3/122302_photo.jpg'
23
+ expect(band.independent).not_to be true
24
+ expect(band.similar.length).to eq 19
25
+ expect(band.links.length).to eq 15
26
+ expect(band.links.count { |l| l[:type] == :official }).to eq 12
27
+ expect(band.links.count { |l| l[:type] == :merchandise }).to eq 3
28
+ expect(band.links.select { |l| l[:type] == :merchandise }.first[:url]).to eq 'http://www.amazon.com/Fifth-Element-Pathfinder/dp/B007MNNCVW'
29
+ expect(band.links.select { |l| l[:type] == :merchandise }.first[:title]).to eq 'Amazon'
30
+ end
31
+
32
+ it 'Lemmy Kilmister has properties' do
33
+ band = MetalArchives::Band.find 32
34
+
35
+ expect(band).to be_instance_of MetalArchives::Band
36
+ expect(band.name).to eq 'Rhapsody of Fire'
37
+ expect(band.aliases).to match %w(Thundercross Rhapsody)
38
+ end
39
+
40
+ it 'maps status' do
41
+ expect(MetalArchives::Parsers::Band.send(:map_status, nil)).to eq ''
42
+ expect(MetalArchives::Parsers::Band.send(:map_status, :active)).to eq 'Active'
43
+ expect(MetalArchives::Parsers::Band.send(:map_status, :split_up)).to eq 'Split-up'
44
+ expect(MetalArchives::Parsers::Band.send(:map_status, :on_hold)).to eq 'On hold'
45
+ expect(MetalArchives::Parsers::Band.send(:map_status, :unknown)).to eq 'Unknown'
46
+ expect(MetalArchives::Parsers::Band.send(:map_status, :changed_name)).to eq 'Changed name'
47
+ expect(MetalArchives::Parsers::Band.send(:map_status, :disputed)).to eq 'Disputed'
48
+
49
+ expect(-> { MetalArchives::Parsers::Band.send(:map_status, :invalid_status) }).to raise_error MetalArchives::Errors::ParserError
50
+ end
51
+
52
+ it 'rewrites URIs' do
53
+ old_endpoint = MetalArchives.config.endpoint
54
+ MetalArchives.config.endpoint = 'https://foo.bar/'
55
+
56
+ band = MetalArchives::Band.find! 122302
57
+
58
+ expect(band.logo.scheme).to eq 'https'
59
+ expect(band.logo.host).to eq 'foo.bar'
60
+
61
+ expect(band.logo.scheme).to eq 'https'
62
+ expect(band.photo.host).to eq 'foo.bar'
63
+
64
+ MetalArchives.config.endpoint = old_endpoint
65
+ end
66
+ end
67
+
68
+ describe 'methods' do
69
+ describe 'find' do
70
+ it 'finds a band' do
71
+ band = MetalArchives::Band.find 3540361100
72
+
73
+ expect(band).not_to be_nil
74
+ expect(band).to be_instance_of MetalArchives::Band
75
+ expect(band.id).to eq 3540361100
76
+ expect(band.name).to eq 'Alquimia'
77
+ expect(band.country).to eq ISO3166::Country['ES']
78
+
79
+ expect(band.logo).to be_instance_of URI::HTTPS
80
+ expect(band.logo.path).to eq '/images/3/5/4/0/3540361100_logo.gif'
81
+
82
+ expect(band.photo).to be_instance_of URI::HTTPS
83
+ expect(band.photo.path).to eq '/images/3/5/4/0/3540361100_photo.jpg'
84
+ end
85
+
86
+ it 'lazily loads' do
87
+ band = MetalArchives::Band.find -1
88
+
89
+ expect(band).to be_instance_of MetalArchives::Band
90
+ end
91
+ end
92
+
93
+ describe 'find!' do
94
+ it 'finds a band' do
95
+ band = MetalArchives::Band.find! 3540361100
96
+
97
+ expect(band).to be_instance_of MetalArchives::Band
98
+ expect(band.name).to eq 'Alquimia'
99
+ end
100
+
101
+ it 'raises on invalid id' do
102
+ expect(-> { MetalArchives::Band.find! -1 }).to raise_error MetalArchives::Errors::APIError
103
+ expect(-> { MetalArchives::Band.find! 0 }).to raise_error MetalArchives::Errors::InvalidIDError
104
+ expect(-> { MetalArchives::Band.find! nil }).to raise_error MetalArchives::Errors::InvalidIDError
105
+ end
106
+ end
107
+
108
+ describe 'find_by' do
109
+ it 'finds a band' do
110
+ band = MetalArchives::Band.find_by :name => 'Falconer'
111
+
112
+ expect(band).to be_instance_of MetalArchives::Band
113
+ expect(band.id).to eq 74
114
+ end
115
+
116
+ it 'returns nil on invalid id' do
117
+ band = MetalArchives::Band.find_by :name => 'SomeNonExistantName'
118
+
119
+ expect(band).to be_nil
120
+ end
121
+ end
122
+
123
+ describe 'find_by!' do
124
+ it 'finds a band' do
125
+ band = MetalArchives::Band.find_by! :name => 'Falconer'
126
+
127
+ expect(band).to be_instance_of MetalArchives::Band
128
+ expect(band.id).to eq 74
129
+ end
130
+
131
+ it 'returns nil on invalid id' do
132
+ band = MetalArchives::Band.find_by! :name => 'SomeNonExistantName'
133
+
134
+ expect(band).to be_nil
135
+ end
136
+ end
137
+
138
+ describe 'search' do
139
+ it 'returns a collection' do
140
+ collection = MetalArchives::Band.search 'Alquimia'
141
+
142
+ expect(collection).to be_instance_of MetalArchives::Collection
143
+ expect(collection.first).to be_instance_of MetalArchives::Band
144
+ end
145
+
146
+ it 'returns an empty collection' do
147
+ expect(MetalArchives::Band.search 'SomeNoneExistantName').to be_empty
148
+ end
149
+
150
+ it 'searches by name' do
151
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia').count).to eq 5
152
+ expect(MetalArchives::Band.search_by(:name => 'Lost Horizon').count).to eq 3
153
+ expect(MetalArchives::Band.search_by(:name => 'Lost Horizon', :exact => true).count).to eq 2
154
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia', :genre => 'Melodic Power').count).to eq 2
155
+ end
156
+
157
+ it 'searches by year' do
158
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia', :year => MetalArchives::Range.new(nil, nil)).count).to eq 5
159
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia', :year => MetalArchives::Range.new(Date.new(2013), nil)).count).to eq 1
160
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia', :year => MetalArchives::Range.new(Date.new(2008), Date.new(2008))).count).to eq 1
161
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia', :year => MetalArchives::Range.new(Date.new(2008), Date.new(2013))).count).to eq 2
162
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia', :year => MetalArchives::Range.new(nil, Date.new(2013))).count).to eq 5
163
+ end
164
+
165
+ it 'searches by country' do
166
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia', :country => ISO3166::Country['ES']).count).to eq 1
167
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia', :country => ISO3166::Country['AR']).count).to eq 3
168
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia', :country => ISO3166::Country['AR']).count).to eq 3
169
+ expect(MetalArchives::Band.search_by(:name => 'Alquimia', :label => 'Mutus Liber').count).to eq 1
170
+ end
171
+ end
172
+
173
+ describe 'all' do
174
+ it 'returns a collection' do
175
+ expect(MetalArchives::Band.all).to be_instance_of MetalArchives::Collection
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ ##
5
+ # Sample model without :id property or :assemble method
6
+ #
7
+ class ModelOne < MetalArchives::BaseModel
8
+ property :property_one
9
+ end
10
+
11
+ ##
12
+ # Sample model without :assemble method
13
+ #
14
+ class ModelTwo < MetalArchives::BaseModel
15
+ property :id
16
+ property :property_one
17
+ end
18
+
19
+ ##
20
+ # Sample complete model
21
+ #
22
+ class ModelThree < MetalArchives::BaseModel
23
+ property :id
24
+ property :property_one
25
+
26
+ def assemble
27
+ { :property_one => 'Property One' }
28
+ end
29
+ end
30
+
31
+ ##
32
+ # Sample complete model
33
+ #
34
+ class ModelFour < MetalArchives::BaseModel
35
+ property :id
36
+ property :property_one
37
+
38
+ def assemble
39
+ { :property_one => 'Property One' }
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Sample invalid model
45
+ #
46
+ class ModelFive < MetalArchives::BaseModel
47
+ property :id
48
+ property :property_one
49
+
50
+ def assemble
51
+ raise MetalArchives::Errors::APIError
52
+ end
53
+ end
54
+
55
+ RSpec.describe MetalArchives::BaseModel do
56
+ it 'requires an :id property' do
57
+ expect(-> { ModelOne.new(:property_one => 'foo') }).to raise_error MetalArchives::Errors::NotImplementedError
58
+ end
59
+
60
+ it 'requires an :assemble method' do
61
+ expect(-> { ModelTwo.new(:id => 'foo').property_one }).to raise_error MetalArchives::Errors::NotImplementedError
62
+ end
63
+
64
+ it 'defines methods' do
65
+ model = ModelTwo.new :id => 'foo'
66
+
67
+ expect(model).to respond_to :id
68
+ expect(model).to respond_to :id=
69
+ expect(model).to respond_to :id?
70
+ expect(model).to respond_to :property_one
71
+ expect(model).to respond_to :property_one=
72
+ expect(model).to respond_to :property_one?
73
+ expect(model).not_to respond_to :property_two
74
+ expect(model).not_to respond_to :property_two=
75
+ expect(model).not_to respond_to :property_two?
76
+ end
77
+
78
+ it 'returns properties' do
79
+ model = ModelThree.new :id => 'foo', :property_one => 'bar'
80
+
81
+ expect(model.id).to eq 'foo'
82
+ expect(model.property_one).to eq 'bar'
83
+ end
84
+
85
+ it 'sets properties' do
86
+ model = ModelThree.new :id => 'foo', :property_one => 'bar'
87
+
88
+ model.id = 'baz'
89
+ model.property_one = 'bat'
90
+
91
+ expect(model.id).to eq 'baz'
92
+ expect(model.property_one).to eq 'bat'
93
+ end
94
+
95
+ it 'checks properties' do
96
+ model = ModelThree.new :id => 'foo', :property_one => 'bar'
97
+ model2 = ModelThree.new :id => 'foo'
98
+ model2.load!
99
+ model2.property_one = nil
100
+
101
+ expect(model.id?).to be true
102
+ expect(model.property_one?).to be true
103
+
104
+ expect(model2.id?).to be true
105
+ expect(model2.property_one?).to be false
106
+ end
107
+
108
+ it 'calls assemble' do
109
+ model = ModelThree.new :id => 'foo'
110
+
111
+ expect(model.id).to eq 'foo'
112
+ expect(model.property_one).to eq 'Property One'
113
+ end
114
+
115
+ it 'lazily loads' do
116
+ model = ModelThree.new :id => 'foo'
117
+
118
+ expect(model).to be_instance_variable_defined '@id'
119
+ expect(model).not_to be_instance_variable_defined '@property_one'
120
+
121
+ model.property_one
122
+
123
+ expect(model).to be_instance_variable_defined '@id'
124
+ expect(model).to be_instance_variable_defined '@property_one'
125
+ expect(model.property_one).to eq 'Property One'
126
+ end
127
+
128
+
129
+ it 'implements the load! operation' do
130
+ model = ModelThree.new :id => 'foo'
131
+
132
+ expect(model).to be_instance_variable_defined '@id'
133
+ expect(model).not_to be_instance_variable_defined '@property_one'
134
+
135
+ model.load!
136
+
137
+ expect(model).to be_instance_variable_defined '@id'
138
+ expect(model).to be_instance_variable_defined '@property_one'
139
+ expect(model.property_one).to eq 'Property One'
140
+ end
141
+
142
+ it 'implements the equal operator' do
143
+ m1 = ModelThree.new :id => 'id_one'
144
+ m2 = ModelThree.new :id => 'id_one'
145
+ m3 = ModelThree.new :id => 'id_two'
146
+ m4 = ModelFour.new :id => 'id_one'
147
+
148
+ expect(m1).to eq m2
149
+ expect(m2).not_to eq m3
150
+ expect(m1).not_to eq m3
151
+ expect(m1).not_to eq m4
152
+ expect(m2).not_to eq m4
153
+ end
154
+
155
+ describe 'loaded?' do
156
+ it 'has a :loaded? method' do
157
+ expect(ModelThree.new).to respond_to :loaded?
158
+ end
159
+
160
+ it 'returns false on lazy load' do
161
+ m = ModelThree.new :id => 'id_one'
162
+
163
+ expect(m).not_to be_loaded
164
+ end
165
+
166
+ it 'returns true on load!' do
167
+ m = ModelThree.new :id => 'id_one'
168
+ m.load!
169
+
170
+ expect(m).to be_loaded
171
+ end
172
+ end
173
+
174
+ describe 'cached?' do
175
+ context 'valid model' do
176
+ it 'has a :cached? method' do
177
+ expect(ModelThree.new).to respond_to :cached?
178
+ end
179
+
180
+ it "doesn't cache lazily loaded objects" do
181
+ m = ModelThree.new :id => 'id_one'
182
+
183
+ expect(m).not_to be_loaded
184
+ expect(m).not_to be_cached
185
+ end
186
+
187
+ it 'caches loaded objects' do
188
+ m = ModelThree.new :id => 'id_one'
189
+ m.load!
190
+
191
+ expect(m).to be_loaded
192
+ expect(m).to be_cached
193
+ end
194
+ end
195
+
196
+ context 'invalid model' do
197
+ it 'has a :cached? method' do
198
+ expect(ModelFive.new).to respond_to :cached?
199
+ end
200
+
201
+ it "doesn't cache lazily loaded objects" do
202
+ m = ModelFive.new :id => 'id_one'
203
+
204
+ expect(m).not_to be_loaded
205
+ expect(m).not_to be_cached
206
+ end
207
+
208
+ it "doesn't cache loaded invalid objects" do
209
+ m = ModelFive.new :id => 'id_one'
210
+ expect(-> { m.load! }).to raise_error MetalArchives::Errors::APIError
211
+
212
+ expect(m).not_to be_loaded
213
+ expect(m).not_to be_cached
214
+ end
215
+ end
216
+ end
217
+ end