muve 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d2c799eb30443546d3054c9dfb9b04069fa8ed21
4
- data.tar.gz: e47754d72235cafaaaf41e5075319e0b7aab1c84
3
+ metadata.gz: 86078fd9dbc468dc0837688bd28c9ffbad043681
4
+ data.tar.gz: 3de1f46815f7ec2cdb1511b8c24445277a06df7a
5
5
  SHA512:
6
- metadata.gz: 58c9bd11811f3b4181e8ff738963f275fffe8b36244d73a3013e0d5fdaaeef7bcdc8e1ca94d9d9f018995274e79680b6471388f0dd3d7dd556fb1f7b07eb7d55
7
- data.tar.gz: abd202a42a8d6da4ebac3409657f9239f4313656028ab64cac55657e57cfd6bf2a632dc02e3453426985232a33e6d811177c3c10948c2c6c077055c4183155ad
6
+ metadata.gz: b83a8681897f5ef214d6aaa3ed729972c38e263bd91e62fa12cc555775fa2cb6e413bad80911d704eb027c553d06c08e2e3ef9b9628fb1e2a5669d11a9a475db
7
+ data.tar.gz: b0fb6001de7b85bb2b60e9fea97f32466e5365d058bcfba4aa65548b1456fa4b14ec9202270c4c04bc4260c633ed2bc33b9a8d38475b1251ac261841d1251c57
data/lib/muve/errors.rb CHANGED
@@ -1,17 +1,19 @@
1
- module MuveError
2
- class MuveStandardError < StandardError; end
3
-
4
- class MuveIncompleteImplementation < MuveStandardError; end
5
-
6
- class MuveInvalidAttributes < MuveStandardError; end
7
-
8
- class MuveInvalidQuery < MuveStandardError; end
9
-
10
- class MuveNotConfigured < MuveStandardError; end
11
-
12
- class MuveNotFound < MuveStandardError; end
13
-
14
- class MuveValidationError < MuveStandardError; end
15
-
16
- class MuveSaveError < MuveStandardError; end
1
+ module Muve
2
+ module Error
3
+ class StandardError < StandardError; end
4
+
5
+ class IncompleteImplementation < StandardError; end
6
+
7
+ class InvalidAttribute < StandardError; end
8
+
9
+ class InvalidQuery < StandardError; end
10
+
11
+ class NotConfigured < StandardError; end
12
+
13
+ class NotFound < StandardError; end
14
+
15
+ class ValidationError < StandardError; end
16
+
17
+ class SaveError < StandardError; end
18
+ end
17
19
  end
data/lib/muve/location.rb CHANGED
@@ -19,6 +19,14 @@ module Muve
19
19
  true
20
20
  end
21
21
 
22
+ def latitude=(value)
23
+ @latitude = value.to_f
24
+ end
25
+
26
+ def longitude=(value)
27
+ @longitude = value.to_f
28
+ end
29
+
22
30
  def random(center, range)
23
31
  end
24
32
  end
data/lib/muve/model.rb CHANGED
@@ -13,7 +13,7 @@ module Muve
13
13
  # implementation
14
14
  # ++
15
15
  module Model
16
- include MuveError
16
+ include Muve::Error
17
17
  include Muve::Helper
18
18
 
19
19
  def initialize(params={})
@@ -27,10 +27,20 @@ module Muve
27
27
  @destroyed = false
28
28
  end
29
29
 
30
+ def reload
31
+ self.send(:populate, extract(adaptor.get(self.class, id))) if id
32
+ self
33
+ end
34
+
30
35
  def self.included(base)
31
36
  base.extend ClassMethods
32
37
  end
33
38
 
39
+ def ==(rival)
40
+ return false unless rival.kind_of? self.class
41
+ self.attributes == rival.attributes
42
+ end
43
+
34
44
  # Initializes the +Muve::Model+ class. Use the +Muve::Model::init+ method
35
45
  # to set a adaptor to take care of the retrieval and storage of resources.
36
46
  def self.init(handler=nil)
@@ -53,28 +63,29 @@ module Muve
53
63
  hash = {}
54
64
  attributes.map { |k, v|
55
65
  if v.respond_to? :to_hash
56
- (hash[k] = v.to_hash(level+1, limit)) if level < limit
66
+ (hash[k.to_sym] = v.to_hash(level+1, limit)) if level < limit
57
67
  else
58
- #(raise MuveAssocError, "#Associated #{v.class} for #{k} must respond to #to_hash or be a Hash") unless v.kind_of? Hash
59
- hash[k] = v
68
+ #(raise AssocError, "#Associated #{v.class} for #{k} must respond to #to_hash or be a Hash") unless v.kind_of? Hash
69
+ hash[k.to_sym] = v
60
70
  end
61
71
  }
62
72
  hash
63
73
  end
64
74
 
65
- # Save a resource and raises an MuveSaveError on failure
75
+ # Save a resource and raises an SaveError on failure
66
76
  def save!
77
+ raise ValidationError, "validation failed" unless valid?
67
78
  create_or_update
68
79
  rescue => e
69
- e.backtrace.each { |err| p err }
70
- raise MuveSaveError, "Save failed because #{e} was raised"
80
+ raise SaveError, "Save failed because #{e} was raised"
71
81
  end
72
82
 
73
83
  # Save a resource
74
84
  def save
75
85
  # TODO: be more verbose about the nature of the failure, if any
76
- raise MuveValidationError, "validation failed" unless valid?
77
- create_or_update
86
+ (create_or_update if valid?) or false
87
+ rescue => e
88
+ false
78
89
  end
79
90
 
80
91
  # Destroy a resource
@@ -115,8 +126,30 @@ module Muve
115
126
  def id
116
127
  @id
117
128
  end
129
+
130
+ # The parameterized identifier of the resource
131
+ def to_param
132
+ id && id.to_s
133
+ end
134
+
135
+ def attributes=(data)
136
+ Helper.symbolize_keys(data).each { |k, v| self.public_send("#{k}=", v) }
137
+ self
138
+ end
139
+
140
+ # Returns a Hash of the attributes for the current resource.
141
+ def attributes
142
+ self.class.extract_attributes(
143
+ resource: self,
144
+ fields: fields,
145
+ invalid_attributes: invalid_attributes,
146
+ id: self.id
147
+ )
148
+ end
118
149
 
119
150
  private
151
+ # Reserved attributes. These keys are not available to the user because
152
+ # they are used to handle plumbing (library internal operations).
120
153
  def invalid_attributes
121
154
  %w(id adaptor)
122
155
  end
@@ -134,25 +167,17 @@ module Muve
134
167
  @new_record = false if details.key? :id
135
168
  end
136
169
 
137
- def serialized_attributes
138
- to_hash
170
+ def storeable_attributes
171
+ hash = {}
172
+ attributes.map { |k, v| hash[k] = convert(v) }
173
+ hash
139
174
  end
140
175
 
141
176
  def create_or_update
142
- result = new_record? ? create(serialized_attributes) : update(serialized_attributes)
177
+ result = new_record? ? create(storeable_attributes) : update(storeable_attributes)
143
178
  self
144
179
  end
145
180
 
146
- # NOTE: not sure we need this
147
- def attributes
148
- self.class.extract_attributes(
149
- resource: self,
150
- fields: fields,
151
- invalid_attributes: invalid_attributes,
152
- id: self.id
153
- )
154
- end
155
-
156
181
  # A manifest of the fields known to the model. The model logic seeks
157
182
  # counsel from this resource to determine which properties to write and
158
183
  # read from the repository.
@@ -164,6 +189,7 @@ module Muve
164
189
  # the new id and un-marking the new_record?
165
190
  def create(attr)
166
191
  @id = adaptor.create(self.class, attr)
192
+ # TODO: deal with unsuccessful #create
167
193
  @new_record = false
168
194
  end
169
195
 
@@ -176,9 +202,17 @@ module Muve
176
202
  self.class.adaptor
177
203
  end
178
204
 
205
+ def extract(storeable)
206
+ self.class.extract(storeable)
207
+ end
208
+
209
+ def convert(resource)
210
+ self.class.convert(resource)
211
+ end
212
+
179
213
  # Class methods exposed to all Muve models
180
214
  module ClassMethods
181
- include MuveError
215
+ include Muve::Error
182
216
  # Configure the adaptor to take care of handling persistence for this
183
217
  # model. The adaptor should extend +Muve::Store+.
184
218
  #
@@ -193,10 +227,18 @@ module Muve
193
227
  # The adaptor currently set to handle persistence for all Muve::Model
194
228
  # classes and instances
195
229
  def adaptor
196
- raise MuveNotConfigured, "the adaptor has not been set" unless (@adaptor || Model.handler)
230
+ raise NotConfigured, "the adaptor has not been set" unless (@adaptor || Model.handler)
197
231
  @adaptor or Model.handler
198
232
  end
199
233
 
234
+ def extract(storeable)
235
+ adaptor.formatter.convert_from_storeable_object(storeable)
236
+ end
237
+
238
+ def convert(resource)
239
+ adaptor.formatter.convert_to_storeable_object(resource)
240
+ end
241
+
200
242
  def connection
201
243
  Muve::Model.connection
202
244
  end
@@ -204,21 +246,27 @@ module Muve
204
246
  def database
205
247
  Muve::Model.database
206
248
  end
249
+
250
+ def model_name
251
+ self.to_s.split("::").last
252
+ end
207
253
 
208
254
  # The container (e.g.: collection, tablename or anything that is analogous
209
255
  # to this construct) of the resource
210
256
  def container
211
- raise MuveError::MuveNotConfigured, "container not defined for #{self}"
257
+ raise Muve::Error::NotConfigured, "container not defined for #{self}"
212
258
  end
213
259
 
260
+ # Returns a Hash of the attributes for the given resource
261
+ # TODO: do we still need this?
214
262
  def extract_attributes(resource: self.new, fields: [], invalid_attributes: [], id: nil)
215
263
  data = {}
216
264
  fields.select{ |k| k != invalid_attributes }.each { |k|
217
265
  # TODO: confirm resource.respond_to? k prior to assigning
218
- data[k.to_sym] = resource.public_send(k)
266
+ data[k.to_sym] = resource.public_send(k) if resource.respond_to? k
219
267
  }
220
268
  if id
221
- data = data.merge(resource.class.adaptor.index_hash id)
269
+ data = data.merge id: id
222
270
  end
223
271
  data
224
272
  end
@@ -226,7 +274,7 @@ module Muve
226
274
  # Finds a resource by id
227
275
  def find(id)
228
276
  result = self.new()
229
- result.send(:populate, self.adaptor.get(self, id))
277
+ result.send(:populate, extract(self.adaptor.get(self, id)))
230
278
  result
231
279
  end
232
280
 
@@ -237,7 +285,7 @@ module Muve
237
285
  (self.adaptor.get(self, nil, params) or []).each do |details|
238
286
  details
239
287
  result = self.new()
240
- result.send(:populate, details)
288
+ result.send(:populate, extract(details))
241
289
  item << result
242
290
  end
243
291
  end
@@ -255,15 +303,18 @@ module Muve
255
303
  # the required private +#fields# method one may specify the known fields
256
304
  # of the resource with one line of code.
257
305
  def with_fields(*args)
306
+ fields = self.new.send(:fields) # TODO: Fix this sloppy mess
258
307
  attr_accessor *args
259
- class_eval "def fields; #{args}; end"
308
+ class_eval "def fields; #{Set.new(fields + args).to_a}; end"
260
309
  end
261
310
 
262
311
  # Creates a new resource and persists it to the datastore
263
- def create(attributes)
264
- resource = self.new(attributes)
265
- resource.save if resource
266
- resource
312
+ def create(attr)
313
+ self.new(attr).save
314
+ end
315
+
316
+ def create!(attr)
317
+ self.new(attr).save!
267
318
  end
268
319
 
269
320
  def destroy_all
data/lib/muve/movement.rb CHANGED
@@ -3,7 +3,25 @@ module Muve
3
3
  include Model
4
4
 
5
5
  with_fields :traveller, :location, :time
6
-
6
+
7
+ def latitude
8
+ location[:latitude] if location
9
+ end
10
+
11
+ def longitude
12
+ location[:longitude] if location
13
+ end
14
+
15
+ def latitude=(value)
16
+ location = {} unless location
17
+ location[:latitude]=(value)
18
+ end
19
+
20
+ def longitude=(value)
21
+ location = {} unless location
22
+ location[:longitude]=(value)
23
+ end
24
+
7
25
  def valid?
8
26
  assocs.each do |assoc|
9
27
  return false unless !assoc.nil? && assoc.valid?
@@ -14,6 +32,15 @@ module Muve
14
32
  true
15
33
  end
16
34
 
35
+ alias_method :lat, :latitude
36
+ alias_method :lat=, :latitude=
37
+ alias_method :lon, :longitude
38
+ alias_method :lon=, :longitude=
39
+ alias_method :lng, :longitude
40
+ alias_method :lng=, :longitude=
41
+ alias_method :long, :longitude
42
+ alias_method :long=, :longitude=
43
+
17
44
  private
18
45
  def assocs
19
46
  [
data/lib/muve/place.rb ADDED
@@ -0,0 +1,12 @@
1
+ module Muve
2
+ class Place
3
+ include Model
4
+
5
+ with_fields :name, :location
6
+
7
+ def valid?
8
+ return false unless location.kind_of?(Muve::Location) && location.valid?
9
+ true
10
+ end
11
+ end
12
+ end
data/lib/muve/store.rb CHANGED
@@ -10,16 +10,29 @@ module Muve
10
10
  # Take a look at +Muve::Store::Mongo+ to find an implementation of a store
11
11
  # adaptor.
12
12
  module Store
13
- include MuveError
13
+ include Muve::Error
14
14
  include Muve::Helper
15
15
 
16
+ module Formatter
17
+ include Muve::Error
18
+ include Muve::Helper
19
+
20
+ def convert_to_storeable_object(resource)
21
+ raise IncompleteImplementation, "convertor to storeable needed"
22
+ end
23
+
24
+ def convert_from_storeable_object(storeable)
25
+ raise IncompleteImplementation, "convertor from storeable needed"
26
+ end
27
+ end
28
+
16
29
  # gets data from the given container matching the provided details
17
30
  #
18
31
  # Given a +Place+ resource the following calls may be acceptable
19
32
  # - +Adaptor.get(Place, 1232) # find one resource where id = 1232+
20
33
  # - +Adaptor.get(Place, nil, { city: 'NYC', rating: 5 })+ # find more
21
34
  def get(resource, id=nil, details=nil)
22
- raise MuveInvalidQuery unless id || details
35
+ raise InvalidQuery unless (id || details)
23
36
 
24
37
  if details
25
38
  find(resource, details)
@@ -31,7 +44,7 @@ module Muve
31
44
  # creates a resource containing the specified details in the repository.
32
45
  # Returns the id of the created object on success, raises an error otherwise
33
46
  def create(resource, details)
34
- raise MuveIncompleteImplementation, "implement a create handler for #{self}"
47
+ raise IncompleteImplementation, "implement a create handler for #{self}"
35
48
  end
36
49
 
37
50
  # removes a resource matching the optional +id+ and +details+
@@ -39,12 +52,12 @@ module Muve
39
52
  # A successful removal operation should returns +true+ while any other
40
53
  # value is considered an error.
41
54
  def delete(resource, id, details=nil)
42
- raise MuveIncompleteImplementation, "implement a delete handler for #{self}"
55
+ raise IncompleteImplementation, "implement a delete handler for #{self}"
43
56
  end
44
57
 
45
58
  # update a resource with the identified by +id+ with the given +details+
46
59
  def update(resource, id, details)
47
- raise MuveIncompleteImplementation, "implement a update handler for #{self}"
60
+ raise IncompleteImplementation, "implement a update handler for #{self}"
48
61
  end
49
62
 
50
63
  # collect a single resource from the repository that matches the given id
@@ -54,7 +67,7 @@ module Muve
54
67
  #
55
68
  # { id: 12, name: 'Spock', organization: 'The Enterprise' }
56
69
  def fetch(resource, id, details={})
57
- raise MuveIncompleteImplementation, "implement a fetch handler for #{self}"
70
+ raise IncompleteImplementation, "implement a fetch handler for #{self}"
58
71
  end
59
72
 
60
73
  # find resources from its repository that match the given id and details
@@ -70,13 +83,13 @@ module Muve
70
83
  # end
71
84
  # end
72
85
  def find(resource, details)
73
- raise MuveIncompleteImplementation, "implement a find handler for #{self}"
86
+ raise IncompleteImplementation, "implement a find handler for #{self}"
74
87
  end
75
88
 
76
89
  # counts the resources matching the details, if any.
77
90
  # Returns a integer that represents the amount of matching entities found.
78
91
  def count(resource, details={})
79
- raise MuveIncompleteImplementation, "implement a count handler for #{self}"
92
+ raise IncompleteImplementation, "implement a count handler for #{self}"
80
93
  end
81
94
 
82
95
  alias_method :destroy, :delete
@@ -86,7 +99,11 @@ module Muve
86
99
  # is the +id+ or +_id+ field, while in other cases the index field may be
87
100
  # different. The store should take care of index naming.
88
101
  def index_hash(index_values)
89
- {}
102
+ raise IncompleteImplementation, "implement the index_hash handler for #{self}"
103
+ end
104
+
105
+ def formatter
106
+ raise IncompleteImplementation, "specify a formatter"
90
107
  end
91
108
  end
92
109
  end
data/lib/muve/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Muve
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
data/lib/muve.rb CHANGED
@@ -18,6 +18,8 @@ module Muve
18
18
  require "muve/helpers"
19
19
  require "muve/model"
20
20
  require "muve/store"
21
+
22
+ require "muve/place"
21
23
  require "muve/location"
22
24
  require "muve/traveller"
23
25
  require "muve/movement"
@@ -47,7 +49,7 @@ module Muve
47
49
  begin
48
50
  @@conn
49
51
  rescue => e
50
- raise MuveNotConfigured, "the connection has not been defined"
52
+ raise NotConfigured, "the connection has not been defined"
51
53
  end
52
54
  end
53
55
 
@@ -56,7 +58,7 @@ module Muve
56
58
  begin
57
59
  @@db
58
60
  rescue => e
59
- raise MuveNotConfigured, "the database has not been defined"
61
+ raise NotConfigured, "the database has not been defined"
60
62
  end
61
63
  end
62
64
  end
@@ -69,4 +71,8 @@ module Muve
69
71
  Model.connection = connection if connection
70
72
  Model.database = database if database
71
73
  end
74
+
75
+ def self.raise_something
76
+ raise NotConfigured
77
+ end
72
78
  end
data/spec/factories.rb CHANGED
@@ -13,4 +13,9 @@ FactoryGirl.define do
13
13
  location { build Muve::Location }
14
14
  time { Time.now - rand(500000) }
15
15
  end
16
+
17
+ factory Muve::Place do
18
+ name { Faker::Venue.name }
19
+ location { build Muve::Location }
20
+ end
16
21
  end
@@ -4,12 +4,15 @@ describe Muve::Location do
4
4
  let(:latitude) { Faker::Geolocation.lat }
5
5
  let(:longitude) { Faker::Geolocation.lng }
6
6
 
7
+ it { expect(Muve::Location.model_name).to eq("Location") }
8
+
7
9
  subject { Muve::Location.new latitude: latitude, longitude: longitude }
8
10
  it { expect(subject.latitude).to eq(latitude) }
9
11
  it { expect(subject.longitude).to eq(longitude) }
10
12
  it { expect(subject.lat).to eq(latitude) }
11
13
  it { expect(subject.long).to eq(longitude) }
12
14
  it { expect(subject.lng).to eq(longitude) }
15
+ it { expect{ subject.to_hash }.not_to raise_error }
13
16
 
14
17
  it { is_expected.to respond_to(:lat) }
15
18
  it { is_expected.to respond_to(:latitude) }
@@ -46,7 +49,7 @@ describe Muve::Location do
46
49
 
47
50
  context "validation" do
48
51
  before(:each) { allow_any_instance_of(Muve::Location).to receive(:create_or_update).and_return(true) }
49
- it { expect{build(Muve::Location, longitude: -181).save}.to raise_error }
50
- it { expect{build(Muve::Location).save}.not_to raise_error }
52
+ it { expect{build(Muve::Location, longitude: -181).save!}.to raise_error }
53
+ it { expect{build(Muve::Location).save!}.not_to raise_error }
51
54
  end
52
55
  end
data/spec/model_spec.rb CHANGED
@@ -101,7 +101,7 @@ describe 'Model' do
101
101
  version: 'failed clone by failed experiment'
102
102
  )
103
103
  )
104
- ).send(:serialized_attributes)).to eq(
104
+ ).send(:to_hash)).to eq(
105
105
  name: 'Stewie Griffin',
106
106
  version: 'our instance of the multiverse',
107
107
  another: {
@@ -121,7 +121,7 @@ describe 'Model' do
121
121
  version: 'failed clone by failed experiment'
122
122
  )
123
123
  )
124
- ).send(:serialized_attributes)).to eq(
124
+ ).send(:to_hash)).to eq(
125
125
  name: 'Stewie Griffin',
126
126
  version: 'our instance of the multiverse',
127
127
  another: {
@@ -158,6 +158,7 @@ describe 'Model' do
158
158
  it { expect(subject.name).to eq('Peter Griffin') }
159
159
  it { expect(subject.description).to eq('Surfin-bird lover') }
160
160
  it { expect(subject.version).to eq('the fat one') }
161
+ it { expect(subject.attributes).to eq({ name: 'Peter Griffin', description: 'Surfin-bird lover', version: 'the fat one', age: nil }) }
161
162
  end
162
163
  end
163
164
  end
@@ -168,8 +169,12 @@ describe 'Model' do
168
169
  expect(Muve::Model.connection).to be(object)
169
170
  end
170
171
 
172
+ it 'allows equality checks between similar-type objects' do
173
+ expect(Resource.new(name: 'Jones')).to eq(Resource.new(name: 'Jones'))
174
+ end
175
+
171
176
  it 'raises a not configured exception when connection is not set' do
172
- configuration_error = MuveError::MuveNotConfigured
177
+ configuration_error = Muve::Error::NotConfigured
173
178
  Muve::Model.remove_class_variable(:@@conn) if Muve::Model.class_variable_defined?(:@@conn)
174
179
 
175
180
  expect { Muve::Model.connection }.to raise_error(configuration_error)
@@ -178,7 +183,7 @@ describe 'Model' do
178
183
  end
179
184
 
180
185
  it 'raises a not configured exception when database is not set' do
181
- configuration_error = MuveError::MuveNotConfigured
186
+ configuration_error = Muve::Error::NotConfigured
182
187
  Muve::Model.remove_class_variable(:@@db) if Muve::Model.class_variable_defined?(:@@db)
183
188
 
184
189
  expect { Muve::Model.database }.to raise_error(configuration_error)
@@ -221,6 +226,16 @@ describe 'Model' do
221
226
  expect(another.description).to eq('blah')
222
227
  end
223
228
 
229
+ it 'allows the modification of attributes' do
230
+ resource = Resource.new(name: 'muve-resource', version: 0)
231
+ expect(resource.name).to eq('muve-resource')
232
+ expect(resource.version).to eq(0)
233
+
234
+ resource.attributes = { name: 'french', version: 1 }
235
+ expect(resource.name).to eq('french')
236
+ expect(resource.version).to eq(1)
237
+ end
238
+
224
239
  # TODO: Study if this is desirable perhaps one would rather prefer setting
225
240
  # seperate adaptors for different models
226
241
  it 'shares the adaptor amongst all its instances' do
@@ -265,6 +280,18 @@ describe 'Model' do
265
280
  end
266
281
  end
267
282
 
283
+ class GenericFormatter
284
+ extend Muve::Store::Formatter
285
+
286
+ def self.convert_to_storeable_object(resource)
287
+ resource
288
+ end
289
+
290
+ def self.convert_from_storeable_object(storeable)
291
+ storeable
292
+ end
293
+ end
294
+
268
295
  class GenericAdaptor
269
296
  extend Muve::Store
270
297
 
@@ -272,6 +299,10 @@ describe 'Model' do
272
299
  hash = { :_id => index_value }
273
300
  { :_id => index_value }
274
301
  end
302
+
303
+ def self.formatter
304
+ GenericFormatter
305
+ end
275
306
  end
276
307
 
277
308
  Resource.adaptor = GenericAdaptor
@@ -302,12 +333,37 @@ describe 'Model' do
302
333
  subject { @res }
303
334
  it_behaves_like "an ActiveRecord-like resource"
304
335
  end
305
-
336
+
306
337
  it 'calls the store create handler upon save' do
307
338
  expect(GenericAdaptor).to receive(:create).once
308
339
  @res.save
309
340
  end
310
341
 
342
+ describe '#reload' do
343
+ it 'does' do
344
+ id = SecureRandom.hex
345
+ initial_attrs = {
346
+ id: id,
347
+ name: 'First',
348
+ version: '0',
349
+ description: 'before'
350
+ }
351
+ final_attrs = {
352
+ id: id,
353
+ name: 'Last',
354
+ version: '99',
355
+ description: 'after'
356
+ }
357
+ allow(GenericAdaptor).to receive(:fetch).and_return(initial_attrs)
358
+ resource = AnotherResource.find(id)
359
+ expect(resource.attributes).to include(initial_attrs)
360
+
361
+ allow(GenericAdaptor).to receive(:fetch).and_return(final_attrs)
362
+ expect(resource.attributes).to include(initial_attrs)
363
+ expect(resource.reload.attributes).to include(final_attrs)
364
+ end
365
+ end
366
+
311
367
  describe '#find' do
312
368
  before(:each) do
313
369
  @id = SecureRandom.hex
@@ -336,6 +392,11 @@ describe 'Model' do
336
392
  it 'returns a record that is not a new record' do
337
393
  expect(AnotherResource.find(@id).new_record?).to be(false)
338
394
  end
395
+
396
+ it 'converts stored data to a workable object' do
397
+ expect(AnotherResource).to receive(:extract).at_least(:once)
398
+ AnotherResource.find(@id)
399
+ end
339
400
  end
340
401
 
341
402
  describe '#where' do
@@ -356,6 +417,11 @@ describe 'Model' do
356
417
  expect(item).to be_a(AnotherResource)
357
418
  end
358
419
  end
420
+
421
+ it 'converts a stored data to a workable object' do
422
+ expect(AnotherResource).to receive(:extract).at_least(:once)
423
+ AnotherResource.where({ name: 'all' }).count
424
+ end
359
425
  end
360
426
 
361
427
  describe '::create' do
@@ -365,8 +431,8 @@ describe 'Model' do
365
431
 
366
432
  it 'creates a new instance' do
367
433
  attributes = { name: 'Bonobo' }
368
- expect(Resource).to receive(:new).with(attributes).once
369
- Resource.create(attributes)
434
+ expect(Resource).to receive(:new).with(attributes).and_return(Resource.new(attributes)).once
435
+ expect(Resource.create(attributes)).to be_a(Resource)
370
436
  end
371
437
 
372
438
  it 'calls the save handler' do
@@ -383,6 +449,7 @@ describe 'Model' do
383
449
  describe '#save' do
384
450
  before(:each) do
385
451
  @res.name = 'first'
452
+ # NOTE: returning @id should not work, return should be a boolean
386
453
  allow(GenericAdaptor).to receive(:create).and_return(@id = SecureRandom.hex)
387
454
  end
388
455
 
@@ -402,6 +469,11 @@ describe 'Model' do
402
469
  it 'is persisted' do
403
470
  expect{ @res.save }.to change{ @res.persisted? }.to(true)
404
471
  end
472
+
473
+ it 'is converted to a storage-friendly format' do
474
+ expect(GenericFormatter).to receive(:convert_to_storeable_object).at_least(1)
475
+ @res.save
476
+ end
405
477
  end
406
478
 
407
479
  describe 'on a existing record' do
@@ -415,6 +487,12 @@ describe 'Model' do
415
487
  @res.name = 'second'
416
488
  @res.save
417
489
  end
490
+
491
+ it 'is converted to a storage-friendly format' do
492
+ expect(GenericFormatter).to receive(:convert_to_storeable_object).at_least(1)
493
+ @res.name = 'something'
494
+ @res.save
495
+ end
418
496
  end
419
497
  end
420
498
 
@@ -469,7 +547,7 @@ describe 'Model' do
469
547
  Resource.find(id)
470
548
  end
471
549
 
472
- it 'converts the attributes to saveable objects' do
550
+ it 'hashes a flat resource' do
473
551
  expect(Resource.new(
474
552
  name: 'Peter Griffin',
475
553
  version: 'dumbass',
@@ -491,7 +569,7 @@ describe 'Model' do
491
569
  )
492
570
  end
493
571
 
494
- it 'converts the attributes to saveable objects' do
572
+ it 'hashes nested resources' do
495
573
  another = AnotherResource.new
496
574
  another.send :populate, {
497
575
  id: SecureRandom.hex,
@@ -508,7 +586,7 @@ describe 'Model' do
508
586
  name: 'Peter Griffin',
509
587
  version: 'dumbass',
510
588
  another: {
511
- _id: another.id,
589
+ id: another.id,
512
590
  name: 'Brian Griffin',
513
591
  version: 'the pretentious one',
514
592
  description: 'Canine, liberal, writer',
@@ -516,5 +594,62 @@ describe 'Model' do
516
594
  }
517
595
  )
518
596
  end
597
+
598
+ it 'returns the attributes on request' do
599
+ another = AnotherResource.new
600
+ another.send :populate, {
601
+ id: SecureRandom.hex,
602
+ name: 'Brian Griffin',
603
+ version: 'the pretentious one',
604
+ description: 'Canine, liberal, writer',
605
+ age: 8
606
+ }
607
+ resource = Resource.new
608
+ id = SecureRandom.hex
609
+ resource.send :populate, {
610
+ id: id,
611
+ name: 'Peter Griffin',
612
+ version: 'dumbass',
613
+ another: another
614
+ }
615
+
616
+ expect(resource.attributes).to eq(
617
+ id: id,
618
+ name: 'Peter Griffin',
619
+ version: 'dumbass',
620
+ another: another
621
+ )
622
+ end
623
+
624
+ it 'converts repopulated data to resources' do
625
+ data = {
626
+ id: SecureRandom.hex,
627
+ name: 'Peter Griffin',
628
+ version: 'dumbass',
629
+ another: {
630
+ id: SecureRandom.hex,
631
+ name: 'Brian Griffin',
632
+ version: 'the pretentious one',
633
+ description: 'Canine, liberal, writer',
634
+ age: 8
635
+ }
636
+ }
637
+ resource = Resource.new
638
+ resource.send(:populate, data)
639
+ end
640
+ end
641
+
642
+ describe "#to_param" do
643
+ it "is equal to the stringified id" do
644
+ object = AnotherResource.new
645
+ object.send :populate, {
646
+ id: SecureRandom.hex,
647
+ name: 'Brian Griffin',
648
+ version: 'the pretentious one',
649
+ description: 'Canine, liberal, writer',
650
+ age: 8
651
+ }
652
+ expect(object.id.to_s).to eq(object.to_param)
653
+ end
519
654
  end
520
655
  end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Muve::Place do
4
+ it { expect(Muve::Place.model_name).to eq "Place" }
5
+
6
+ it { is_expected.to respond_to :location }
7
+ it { is_expected.to respond_to :name }
8
+
9
+ it 'is invalid when the location is not a Location' do
10
+ expect(build Muve::Place, location: Object.new).to be_invalid
11
+ expect(build Muve::Place, location: rand).to be_invalid
12
+ expect(build Muve::Place, location: build(Muve::Location, latitude: -100)).to be_invalid
13
+ expect(build Muve::Place, location: build(Muve::Location)).to be_valid
14
+ end
15
+ end
data/spec/store_spec.rb CHANGED
@@ -12,6 +12,10 @@ describe Muve::Store do
12
12
  class GenericStore
13
13
  extend Muve::Store
14
14
  end
15
+
16
+ class GenericFormatter
17
+ extend Muve::Store::Formatter
18
+ end
15
19
  end
16
20
 
17
21
  it 'provides methods to get a resource' do
@@ -33,6 +37,10 @@ describe Muve::Store do
33
37
  expect(GenericStore).to respond_to(:update)
34
38
  end
35
39
 
40
+ it 'has a formatter' do
41
+ expect(GenericStore).to respond_to(:formatter)
42
+ end
43
+
36
44
  it 'raises incomplete implementation errors on non-implemented methods' do
37
45
  %w(create delete update fetch find).each do |method|
38
46
  expect{
@@ -41,8 +49,10 @@ describe Muve::Store do
41
49
  else
42
50
  GenericStore.send(method, 'resource', nil)
43
51
  end
44
- }.to raise_error(MuveError::MuveIncompleteImplementation)
52
+ }.to raise_error(Muve::Error::IncompleteImplementation)
45
53
  end
54
+
55
+ expect{GenericStore.send(:formatter)}.to raise_error(Muve::Error::IncompleteImplementation)
46
56
  end
47
57
 
48
58
  it 'attempts to fetch a resource if the id is given' do
@@ -60,4 +70,9 @@ describe Muve::Store do
60
70
  GenericStore.get(Resource)
61
71
  }.to raise_error
62
72
  end
73
+
74
+ describe "formatter" do
75
+ it { expect(GenericFormatter).to respond_to(:convert_to_storeable_object) }
76
+ it { expect(GenericFormatter).to respond_to(:convert_from_storeable_object) }
77
+ end
63
78
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: muve
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Asabina
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-26 00:00:00.000000000 Z
11
+ date: 2014-09-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Basic helpers to be used with Muvement
14
14
  email:
@@ -28,6 +28,7 @@ files:
28
28
  - lib/muve/location.rb
29
29
  - lib/muve/model.rb
30
30
  - lib/muve/movement.rb
31
+ - lib/muve/place.rb
31
32
  - lib/muve/store.rb
32
33
  - lib/muve/traveller.rb
33
34
  - lib/muve/version.rb
@@ -38,6 +39,7 @@ files:
38
39
  - spec/model_spec.rb
39
40
  - spec/movement_spec.rb
40
41
  - spec/muve_spec.rb
42
+ - spec/place_spec.rb
41
43
  - spec/spec_helper.rb
42
44
  - spec/store_spec.rb
43
45
  - spec/traveller_spec.rb
@@ -72,6 +74,7 @@ test_files:
72
74
  - spec/model_spec.rb
73
75
  - spec/movement_spec.rb
74
76
  - spec/muve_spec.rb
77
+ - spec/place_spec.rb
75
78
  - spec/spec_helper.rb
76
79
  - spec/store_spec.rb
77
80
  - spec/traveller_spec.rb