api_resource 0.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/api_resource.gemspec +4 -74
- data/coverage/assets/0.5.3/app.js +88 -0
- data/coverage/assets/0.5.3/fancybox/blank.gif +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_close.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_loading.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_nav_left.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_nav_right.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_shadow_e.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_shadow_n.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_shadow_ne.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_shadow_nw.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_shadow_s.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_shadow_se.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_shadow_sw.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_shadow_w.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_title_left.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_title_main.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_title_over.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancy_title_right.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancybox-x.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancybox-y.png +0 -0
- data/coverage/assets/0.5.3/fancybox/fancybox.png +0 -0
- data/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.css +363 -0
- data/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.pack.js +44 -0
- data/coverage/assets/0.5.3/favicon_green.png +0 -0
- data/coverage/assets/0.5.3/favicon_red.png +0 -0
- data/coverage/assets/0.5.3/favicon_yellow.png +0 -0
- data/coverage/assets/0.5.3/highlight.css +129 -0
- data/coverage/assets/0.5.3/highlight.pack.js +1 -0
- data/coverage/assets/0.5.3/jquery-1.6.2.min.js +18 -0
- data/coverage/assets/0.5.3/jquery.dataTables.min.js +152 -0
- data/coverage/assets/0.5.3/jquery.timeago.js +141 -0
- data/coverage/assets/0.5.3/jquery.url.js +174 -0
- data/coverage/assets/0.5.3/loading.gif +0 -0
- data/coverage/assets/0.5.3/magnify.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.5.3/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.5.3/smoothness/jquery-ui-1.8.4.custom.css +295 -0
- data/coverage/assets/0.5.3/stylesheet.css +383 -0
- data/coverage/index.html +3573 -0
- data/lib/api_resource/associations/abstract_scope.rb +191 -0
- data/lib/api_resource/associations/association_scope.rb +47 -0
- data/lib/api_resource/associations/belongs_to_remote_object_proxy.rb +5 -6
- data/lib/api_resource/associations/has_many_remote_object_proxy.rb +5 -8
- data/lib/api_resource/associations/has_one_remote_object_proxy.rb +12 -13
- data/lib/api_resource/associations/multi_object_proxy.rb +65 -39
- data/lib/api_resource/associations/resource_scope.rb +6 -17
- data/lib/api_resource/associations/scope.rb +23 -121
- data/lib/api_resource/associations/single_object_proxy.rb +41 -50
- data/lib/api_resource/associations.rb +32 -11
- data/lib/api_resource/attributes.rb +108 -69
- data/lib/api_resource/base.rb +114 -106
- data/lib/api_resource/local.rb +1 -1
- data/lib/api_resource/model_errors.rb +9 -6
- data/lib/api_resource/scopes.rb +53 -16
- data/lib/api_resource.rb +3 -1
- data/spec/lib/api_resource_spec.rb +3 -7
- data/spec/lib/associations/association_scope_spec.rb +19 -0
- data/spec/lib/associations_spec.rb +251 -162
- data/spec/lib/attributes_spec.rb +33 -15
- data/spec/lib/base_spec.rb +302 -64
- data/spec/lib/callbacks_spec.rb +4 -2
- data/spec/lib/local_spec.rb +5 -1
- data/spec/spec_helper.rb +2 -3
- data/spec/support/mocks/association_mocks.rb +9 -1
- data/spec/support/requests/association_requests.rb +5 -5
- data/spec/support/requests/test_resource_requests.rb +16 -4
- data/spec/tmp/api_resource_test_db.sqlite +0 -0
- metadata +68 -22
- data/.document +0 -5
- data/.rspec +0 -5
- data/.travis.yml +0 -4
- data/lib/api_resource/associations/association_proxy.rb +0 -121
- data/lib/api_resource/associations/dynamic_resource_scope.rb +0 -23
- data/lib/api_resource/associations/generic_scope.rb +0 -68
- data/lib/api_resource/associations/multi_argument_resource_scope.rb +0 -15
- data/lib/api_resource/associations/relation_scope.rb +0 -25
data/lib/api_resource/base.rb
CHANGED
@@ -25,11 +25,15 @@ module ApiResource
|
|
25
25
|
|
26
26
|
class_attribute :primary_key
|
27
27
|
self.primary_key = "id"
|
28
|
+
|
29
|
+
delegate :logger, :to => ApiResource
|
28
30
|
|
29
31
|
class << self
|
30
32
|
|
31
33
|
# writers - accessors with defaults were not working
|
32
34
|
attr_writer :element_name, :collection_name
|
35
|
+
|
36
|
+
delegate :logger, :to => ApiResource
|
33
37
|
|
34
38
|
def inherited(klass)
|
35
39
|
# Call the methods of the superclass to make sure inheritable accessors and the like have been inherited
|
@@ -44,33 +48,38 @@ module ApiResource
|
|
44
48
|
EOE
|
45
49
|
true
|
46
50
|
end
|
51
|
+
|
52
|
+
def resource_definition
|
53
|
+
@resource_definition
|
54
|
+
end
|
55
|
+
|
47
56
|
# This makes a request to new_element_path
|
48
57
|
def set_class_attributes_upon_load
|
49
58
|
return true if self == ApiResource::Base
|
50
59
|
begin
|
51
|
-
|
60
|
+
@resource_definition = self.connection.get(
|
52
61
|
self.new_element_path, self.headers
|
53
62
|
)
|
54
63
|
# Attributes go first
|
55
|
-
if
|
64
|
+
if resource_definition["attributes"]
|
56
65
|
|
57
66
|
define_attributes(
|
58
|
-
*(
|
67
|
+
*(resource_definition["attributes"]["public"] || [])
|
59
68
|
)
|
60
69
|
define_protected_attributes(
|
61
|
-
*(
|
70
|
+
*(resource_definition["attributes"]["protected"] || [])
|
62
71
|
)
|
63
72
|
|
64
73
|
end
|
65
74
|
# Then scopes
|
66
|
-
if
|
67
|
-
|
75
|
+
if resource_definition["scopes"]
|
76
|
+
resource_definition["scopes"].each_pair do |scope_name, opts|
|
68
77
|
self.scope(scope_name, opts)
|
69
78
|
end
|
70
79
|
end
|
71
80
|
# Then associations
|
72
|
-
if
|
73
|
-
|
81
|
+
if resource_definition["associations"]
|
82
|
+
resource_definition["associations"].each_pair do |key, hash|
|
74
83
|
hash.each_pair do |assoc_name, assoc_options|
|
75
84
|
self.send(key, assoc_name, assoc_options)
|
76
85
|
end
|
@@ -81,15 +90,14 @@ module ApiResource
|
|
81
90
|
# define the basic methods but we need to override all the setters
|
82
91
|
# so we do dirty tracking
|
83
92
|
attrs = []
|
84
|
-
if
|
85
|
-
attrs +=
|
93
|
+
if resource_definition["attributes"] && resource_definition["attributes"]["public"]
|
94
|
+
attrs += resource_definition["attributes"]["public"].collect{|v|
|
86
95
|
v.is_a?(Array) ? v.first : v
|
87
96
|
}.flatten
|
88
97
|
end
|
89
|
-
if
|
90
|
-
attrs +=
|
98
|
+
if resource_definition["associations"]
|
99
|
+
attrs += resource_definition["associations"].values.collect(&:keys).flatten
|
91
100
|
end
|
92
|
-
define_attribute_methods(attrs)
|
93
101
|
|
94
102
|
# Swallow up any loading errors because the site may be incorrect
|
95
103
|
rescue Exception => e
|
@@ -114,29 +122,32 @@ module ApiResource
|
|
114
122
|
# load our resource definition to make sure we know what this class
|
115
123
|
# responds to
|
116
124
|
def respond_to?(*args)
|
117
|
-
self.
|
125
|
+
self.load_resource_definition
|
118
126
|
super
|
119
127
|
end
|
120
128
|
|
121
|
-
def
|
122
|
-
unless instance_variable_defined?(:@
|
123
|
-
|
129
|
+
def load_resource_definition
|
130
|
+
unless instance_variable_defined?(:@resource_definition)
|
131
|
+
# set to not nil so we don't get an infinite loop
|
132
|
+
@resource_definition = true
|
124
133
|
self.set_class_attributes_upon_load
|
134
|
+
return true
|
125
135
|
end
|
126
|
-
|
136
|
+
# we didn't do anything
|
137
|
+
false
|
127
138
|
end
|
128
139
|
|
129
|
-
def
|
140
|
+
def reload_resource_definition
|
130
141
|
# clear the public_attribute_names, protected_attribute_names
|
131
|
-
if instance_variable_defined?(:@
|
132
|
-
remove_instance_variable(:@
|
142
|
+
if instance_variable_defined?(:@resource_definition)
|
143
|
+
remove_instance_variable(:@resource_definition)
|
133
144
|
self.clear_attributes
|
134
145
|
self.clear_related_objects
|
135
146
|
end
|
136
|
-
self.
|
147
|
+
self.load_resource_definition
|
137
148
|
end
|
138
149
|
# backwards compatibility
|
139
|
-
alias_method :reload_class_attributes, :
|
150
|
+
alias_method :reload_class_attributes, :reload_resource_definition
|
140
151
|
|
141
152
|
def token_with_new_token_set=(new_token)
|
142
153
|
self.token_without_new_token_set = new_token
|
@@ -163,7 +174,7 @@ module ApiResource
|
|
163
174
|
|
164
175
|
# reset class attributes and try to reload them if the site changed
|
165
176
|
unless self.site.to_s == old_site
|
166
|
-
self.
|
177
|
+
self.reload_resource_definition
|
167
178
|
end
|
168
179
|
|
169
180
|
return site
|
@@ -219,12 +230,17 @@ module ApiResource
|
|
219
230
|
end
|
220
231
|
|
221
232
|
def prefix=(value = '/')
|
222
|
-
prefix_call = value.gsub(/:\w+/) { |key|
|
233
|
+
prefix_call = value.gsub(/:\w+/) { |key|
|
234
|
+
"\#{URI.escape options[#{key}].to_s}"
|
235
|
+
}
|
223
236
|
@prefix_parameters = nil
|
224
237
|
silence_warnings do
|
225
238
|
instance_eval <<-EOE, __FILE__, __LINE__ + 1
|
226
239
|
def prefix_source() "#{value}" end
|
227
|
-
def prefix(options={})
|
240
|
+
def prefix(options={})
|
241
|
+
ret = "#{prefix_call}"
|
242
|
+
ret =~ Regexp.new(Regexp.escape("//")) ? "/" : ret
|
243
|
+
end
|
228
244
|
EOE
|
229
245
|
end
|
230
246
|
rescue Exception => e
|
@@ -258,22 +274,20 @@ module ApiResource
|
|
258
274
|
end
|
259
275
|
end
|
260
276
|
|
261
|
-
#
|
262
|
-
def new_element_path
|
263
|
-
|
277
|
+
# path to find
|
278
|
+
def new_element_path(prefix_options = {})
|
279
|
+
File.join(
|
280
|
+
self.prefix(prefix_options),
|
281
|
+
self.collection_name,
|
282
|
+
"new.#{format.extension}"
|
283
|
+
)
|
264
284
|
end
|
265
285
|
|
266
286
|
def collection_path(prefix_options = {}, query_options = nil)
|
267
287
|
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
|
268
288
|
|
269
|
-
#
|
270
|
-
#
|
271
|
-
if prefix(prefix_options) =~ /\/\/$/
|
272
|
-
"/#{collection_name}.#{format.extension}#{query_string(query_options)}"
|
273
|
-
else
|
274
|
-
# Fall back on this rather than search without the id
|
275
|
-
"#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
|
276
|
-
end
|
289
|
+
# Fall back on this rather than search without the id
|
290
|
+
"#{prefix(prefix_options)}#{collection_name}.#{format.extension}#{query_string(query_options)}"
|
277
291
|
end
|
278
292
|
|
279
293
|
def build(attributes = {})
|
@@ -289,6 +303,10 @@ module ApiResource
|
|
289
303
|
# where options can be standard rails options or :expires_in.
|
290
304
|
# If :expires_in is set, it caches it for expires_in seconds.
|
291
305
|
def find(*arguments)
|
306
|
+
|
307
|
+
# make sure we have class data before loading
|
308
|
+
self.load_resource_definition
|
309
|
+
|
292
310
|
scope = arguments.slice!(0)
|
293
311
|
options = arguments.slice!(0) || {}
|
294
312
|
|
@@ -343,13 +361,29 @@ module ApiResource
|
|
343
361
|
def delete(id, options = {})
|
344
362
|
connection.delete(element_path(id, options))
|
345
363
|
end
|
346
|
-
|
364
|
+
|
365
|
+
def instantiate_collection(collection)
|
366
|
+
collection.collect{|record|
|
367
|
+
instantiate_record(record)
|
368
|
+
}
|
369
|
+
end
|
370
|
+
|
371
|
+
def instantiate_record(record)
|
372
|
+
self.load_resource_definition
|
373
|
+
ret = self.allocate
|
374
|
+
ret.instance_variable_set(
|
375
|
+
:@attributes, record.with_indifferent_access
|
376
|
+
)
|
377
|
+
ret.instance_variable_set(
|
378
|
+
:@attributes_cache, HashWithIndifferentAccess.new
|
379
|
+
)
|
380
|
+
ret
|
381
|
+
end
|
382
|
+
|
347
383
|
protected
|
348
384
|
def method_missing(meth, *args, &block)
|
349
385
|
# make one attempt to load remote attrs
|
350
|
-
|
351
|
-
self.set_class_attributes_upon_load
|
352
|
-
self.instance_variable_set(:@class_data, true)
|
386
|
+
if self.load_resource_definition
|
353
387
|
return self.send(meth, *args, &block)
|
354
388
|
end
|
355
389
|
super
|
@@ -395,15 +429,6 @@ module ApiResource
|
|
395
429
|
instantiate_record(connection.get(path, headers))
|
396
430
|
end
|
397
431
|
|
398
|
-
def instantiate_collection(collection)
|
399
|
-
collection.collect! { |record| instantiate_record(record) }
|
400
|
-
end
|
401
|
-
|
402
|
-
def instantiate_record(record)
|
403
|
-
new(record)
|
404
|
-
end
|
405
|
-
|
406
|
-
|
407
432
|
# Accepts a URI and creates the site URI from that.
|
408
433
|
def create_site_uri_from(site)
|
409
434
|
site.is_a?(URI) ? site.dup : uri_parser.parse(site)
|
@@ -443,10 +468,13 @@ module ApiResource
|
|
443
468
|
end
|
444
469
|
|
445
470
|
def initialize(attributes = {})
|
471
|
+
# call super's initialize to set up any variables that we need
|
472
|
+
super(attributes)
|
446
473
|
# if we initialize this class, load the attributes
|
447
|
-
self.class.
|
448
|
-
# Now we can make a call to setup the inheriting
|
449
|
-
|
474
|
+
self.class.load_resource_definition
|
475
|
+
# Now we can make a call to setup the inheriting
|
476
|
+
# klass with its attributes
|
477
|
+
self.attributes = attributes
|
450
478
|
end
|
451
479
|
|
452
480
|
def new?
|
@@ -459,12 +487,12 @@ module ApiResource
|
|
459
487
|
end
|
460
488
|
|
461
489
|
def id
|
462
|
-
self.
|
490
|
+
self.read_attribute(self.class.primary_key)
|
463
491
|
end
|
464
492
|
|
465
493
|
# Bypass dirty tracking for this field
|
466
494
|
def id=(id)
|
467
|
-
attributes[self.class.primary_key] = id
|
495
|
+
@attributes[self.class.primary_key] = id
|
468
496
|
end
|
469
497
|
|
470
498
|
def ==(other)
|
@@ -480,9 +508,7 @@ module ApiResource
|
|
480
508
|
end
|
481
509
|
|
482
510
|
def dup
|
483
|
-
self.class.
|
484
|
-
resource.attributes = self.attributes
|
485
|
-
end
|
511
|
+
self.class.instantiate_record(self.attributes)
|
486
512
|
end
|
487
513
|
|
488
514
|
def update_attributes(attrs)
|
@@ -507,8 +533,15 @@ module ApiResource
|
|
507
533
|
end
|
508
534
|
|
509
535
|
def reload
|
510
|
-
|
511
|
-
self.
|
536
|
+
# find the record from the remote service
|
537
|
+
reloaded = self.class.find(self.id)
|
538
|
+
|
539
|
+
# clear out the attributes cache
|
540
|
+
@attributes_cache = HashWithIndifferentAccess.new
|
541
|
+
# set up our attributes cache on our record
|
542
|
+
@attributes = reloaded.instance_variable_get(:@attributes)
|
543
|
+
|
544
|
+
reloaded
|
512
545
|
end
|
513
546
|
|
514
547
|
def to_param
|
@@ -531,56 +564,13 @@ module ApiResource
|
|
531
564
|
self.class.prefix_source.scan(/\:(\w+)/).collect{|match| match.first.to_sym}
|
532
565
|
end
|
533
566
|
|
534
|
-
def load(attributes)
|
535
|
-
return if attributes.nil?
|
536
|
-
raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
|
537
|
-
|
538
|
-
attributes.symbolize_keys.each do |key, value|
|
539
|
-
# If this attribute doesn't exist define it as a protected attribute
|
540
|
-
self.class.define_protected_attributes(key) unless self.respond_to?(key)
|
541
|
-
#self.send("#{key}_will_change!") if self.respond_to?("#{key}_will_change!")
|
542
|
-
self.attributes[key] =
|
543
|
-
case value
|
544
|
-
when Array
|
545
|
-
if self.has_many?(key)
|
546
|
-
MultiObjectProxy.new(self.has_many_class_name(key), value)
|
547
|
-
elsif self.association?(key)
|
548
|
-
raise ArgumentError, "Expected a hash value or nil, got: #{value.inspect}"
|
549
|
-
else
|
550
|
-
typecast_attribute(key, value)
|
551
|
-
end
|
552
|
-
when Hash
|
553
|
-
if self.has_many?(key)
|
554
|
-
MultiObjectProxy.new(self.has_many_class_name(key), value)
|
555
|
-
elsif self.association?(key)
|
556
|
-
#binding.pry
|
557
|
-
SingleObjectProxy.new(self.association_class_name(key), value)
|
558
|
-
else
|
559
|
-
typecast_attribute(key, value)
|
560
|
-
end
|
561
|
-
when NilClass
|
562
|
-
# If it's nil and an association then create a blank object
|
563
|
-
if self.has_many?(key)
|
564
|
-
return MultiObjectProxy.new(self.has_many_class_name(key), [])
|
565
|
-
elsif self.association?(key)
|
566
|
-
SingleObjectProxy.new(self.association_class_name(key), value)
|
567
|
-
end
|
568
|
-
else
|
569
|
-
raise ArgumentError, "expected an array or a hash for the association #{key}, got: #{value.inspect}" if self.association?(key)
|
570
|
-
typecast_attribute(key, value)
|
571
|
-
end
|
572
|
-
end
|
573
|
-
return self
|
574
|
-
end
|
575
|
-
|
576
567
|
# Override to_s and inspect so they only show attributes
|
577
568
|
# and not associations, this prevents force loading of associations
|
578
569
|
# when we call to_s or inspect on a descendent of base but allows it if we
|
579
570
|
# try to evaluate an association directly
|
580
571
|
def to_s
|
581
|
-
return "#<#{self.class}:#{(self.object_id * 2).to_s(16)} @attributes=#{self.attributes
|
572
|
+
return "#<#{self.class}:#{(self.object_id * 2).to_s(16)} @attributes=#{self.attributes}"
|
582
573
|
end
|
583
|
-
|
584
574
|
alias_method :inspect, :to_s
|
585
575
|
|
586
576
|
# Methods for serialization as json or xml, relying on the serializable_hash method
|
@@ -593,11 +583,17 @@ module ApiResource
|
|
593
583
|
end
|
594
584
|
|
595
585
|
def serializable_hash(options = {})
|
586
|
+
|
596
587
|
action = options[:action]
|
588
|
+
|
597
589
|
include_nil_attributes = options[:include_nil_attributes]
|
590
|
+
|
598
591
|
options[:include_associations] = options[:include_associations] ? options[:include_associations].symbolize_array : self.changes.keys.symbolize_array.select{|k| self.association?(k)}
|
592
|
+
|
599
593
|
options[:include_extras] = options[:include_extras] ? options[:include_extras].symbolize_array : []
|
594
|
+
|
600
595
|
options[:except] ||= []
|
596
|
+
|
601
597
|
ret = self.attributes.inject({}) do |accum, (key,val)|
|
602
598
|
# If this is an association and it's in include_associations then include it
|
603
599
|
if options[:include_extras].include?(key.to_sym)
|
@@ -614,7 +610,13 @@ module ApiResource
|
|
614
610
|
end
|
615
611
|
end
|
616
612
|
options[:include_associations].each do |assoc|
|
617
|
-
|
613
|
+
if self.association?(assoc)
|
614
|
+
ret[assoc] = self.send(assoc).serializable_hash({
|
615
|
+
:include_id => true,
|
616
|
+
:include_nil_attributes => include_nil_attributes,
|
617
|
+
:action => action
|
618
|
+
})
|
619
|
+
end
|
618
620
|
end
|
619
621
|
# include id - this is for nested updates
|
620
622
|
ret[:id] = self.id if options[:include_id] && !self.new?
|
@@ -627,7 +629,13 @@ module ApiResource
|
|
627
629
|
end
|
628
630
|
|
629
631
|
def load_attributes_from_response(response)
|
630
|
-
|
632
|
+
if response.present?
|
633
|
+
@attributes_cache = {}
|
634
|
+
@attributes = @attributes.merge(
|
635
|
+
response.with_indifferent_access
|
636
|
+
)
|
637
|
+
end
|
638
|
+
response
|
631
639
|
end
|
632
640
|
|
633
641
|
def element_path(id, prefix_override_options = {}, query_options = nil)
|
data/lib/api_resource/local.rb
CHANGED
@@ -4,11 +4,16 @@ module ApiResource
|
|
4
4
|
|
5
5
|
def from_array(messages, save_cache = false)
|
6
6
|
clear unless save_cache
|
7
|
-
humanized_attributes = @base.attributes.keys.inject({})
|
7
|
+
humanized_attributes = @base.attributes.keys.inject({}){|h, attr_name|
|
8
|
+
h.update(attr_name.to_s.humanize => attr_name)
|
9
|
+
}
|
8
10
|
messages.each do |message|
|
9
11
|
attr_message = humanized_attributes.keys.detect do |attr_name|
|
10
12
|
if message[0,attr_name.size + 1] == "#{attr_name} "
|
11
|
-
add
|
13
|
+
self.add(
|
14
|
+
humanized_attributes[attr_name],
|
15
|
+
message[(attr_name.size + 1)..-1]
|
16
|
+
)
|
12
17
|
end
|
13
18
|
end
|
14
19
|
end
|
@@ -18,7 +23,7 @@ module ApiResource
|
|
18
23
|
clear unless save_cache
|
19
24
|
messages.each do |attr, message_array|
|
20
25
|
message_array.each do |message|
|
21
|
-
add
|
26
|
+
self.add(attr, message)
|
22
27
|
end
|
23
28
|
end
|
24
29
|
end
|
@@ -61,10 +66,8 @@ module ApiResource
|
|
61
66
|
elsif error_data.is_a?(Array)
|
62
67
|
self.errors.from_array(error_data)
|
63
68
|
else
|
64
|
-
raise
|
69
|
+
raise "Invalid response for invalid object: expected an array or hash got #{remote_errors}"
|
65
70
|
end
|
66
|
-
rescue Exception
|
67
|
-
raise "Invalid response for invalid object: expected an array or hash got #{remote_errors}"
|
68
71
|
end
|
69
72
|
|
70
73
|
# This method runs any local validations but not remote ones
|
data/lib/api_resource/scopes.rb
CHANGED
@@ -1,35 +1,72 @@
|
|
1
1
|
module ApiResource
|
2
2
|
module Scopes
|
3
|
+
|
3
4
|
extend ActiveSupport::Concern
|
4
5
|
|
5
|
-
|
6
6
|
module ClassMethods
|
7
7
|
def scopes
|
8
8
|
return self.related_objects[:scopes]
|
9
9
|
end
|
10
10
|
|
11
|
-
# Called by base.rb
|
12
|
-
# @param name is the name of the scope from the json
|
13
|
-
# @param hsh is always a hash with the arguments for the scope
|
14
|
-
def scope(name, hsh)
|
15
|
-
raise ArgumentError, "Expecting an attributes hash given #{hsh.inspect}" unless hsh.is_a?(Hash)
|
16
|
-
self.related_objects[:scopes][name.to_sym] = hsh
|
17
|
-
# we also need to define a class method for each scope
|
18
|
-
self.instance_eval <<-EOE, __FILE__, __LINE__ + 1
|
19
|
-
def #{name}(*args)
|
20
|
-
return #{ApiResource::Associations::ResourceScope.class_factory(hsh)}.new(self, :#{name}, *args)
|
21
|
-
end
|
22
|
-
EOE
|
23
|
-
end
|
24
|
-
|
25
11
|
def scope?(name)
|
26
|
-
self.related_objects[:scopes]
|
12
|
+
self.related_objects[:scopes].has_key?(name.to_sym)
|
27
13
|
end
|
28
14
|
|
29
15
|
def scope_attributes(name)
|
30
16
|
raise "No such scope #{name}" unless self.scope?(name)
|
31
17
|
self.related_objects[:scopes][name.to_sym]
|
32
18
|
end
|
19
|
+
|
20
|
+
# Called by base.rb
|
21
|
+
# @param scope_name is the scope_name of the scope from the json
|
22
|
+
# e.g. paged
|
23
|
+
#
|
24
|
+
# @param scope_definition is always a hash with the arguments for the scope
|
25
|
+
# e.g. {:page => "req", "per_page" => "opt"}
|
26
|
+
def scope(scope_name, scope_definition)
|
27
|
+
|
28
|
+
unless scope_definition.is_a?(Hash)
|
29
|
+
raise ArgumentError, "Expecting an attributes hash given #{scope_definition.inspect}"
|
30
|
+
end
|
31
|
+
|
32
|
+
self.related_objects[:scopes][scope_name.to_sym] = scope_definition
|
33
|
+
|
34
|
+
self.class_eval do
|
35
|
+
|
36
|
+
define_singleton_method(scope_name) do |*args|
|
37
|
+
|
38
|
+
arg_names = scope_definition.keys
|
39
|
+
arg_types = scope_definition.values
|
40
|
+
|
41
|
+
finder_opts = {
|
42
|
+
scope_name => {}
|
43
|
+
}
|
44
|
+
|
45
|
+
arg_names.each_with_index do |arg_name, i|
|
46
|
+
|
47
|
+
# If we are dealing with a scope with multiple args
|
48
|
+
if arg_types[i] == :rest
|
49
|
+
finder_opts[scope_name][arg_name] =
|
50
|
+
args.slice(i, args.count)
|
51
|
+
# Else we are only dealing with a single argument
|
52
|
+
else
|
53
|
+
if arg_types[i] == :req || args[i].present?
|
54
|
+
finder_opts[scope_name][arg_name] = args[i]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# if we have nothing at this point we should just pass 'true'
|
60
|
+
if finder_opts[scope_name] == {}
|
61
|
+
finder_opts[scope_name] = true
|
62
|
+
end
|
63
|
+
|
64
|
+
ApiResource::Associations::ResourceScope.new(
|
65
|
+
self, finder_opts
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
33
70
|
end
|
34
71
|
|
35
72
|
def scopes
|
data/lib/api_resource.rb
CHANGED
@@ -72,7 +72,9 @@ module ApiResource
|
|
72
72
|
def self.cache(reset = false)
|
73
73
|
@cache = nil if reset
|
74
74
|
@cache ||= begin
|
75
|
-
|
75
|
+
defined?(Rails) ? Rails.cache : ActiveSupport::Cache::MemoryStore.new
|
76
|
+
rescue
|
77
|
+
ActiveSupport::Cache::MemoryStore.new
|
76
78
|
end
|
77
79
|
end
|
78
80
|
|
@@ -4,13 +4,9 @@ describe ApiResource do
|
|
4
4
|
|
5
5
|
context ".cache" do
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
example.run
|
11
|
-
ensure
|
12
|
-
Rails = old_rails
|
13
|
-
ApiResource
|
7
|
+
after(:each) do
|
8
|
+
if defined?(Rails)
|
9
|
+
Object.send(:remove_const, :Rails)
|
14
10
|
end
|
15
11
|
end
|
16
12
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ApiResource::Associations::AssociationScope do
|
4
|
+
|
5
|
+
context "#remote_path" do
|
6
|
+
|
7
|
+
it "should provide access to its remote path when
|
8
|
+
instantiated through a parent" do
|
9
|
+
|
10
|
+
test_resource = TestResource.find(1)
|
11
|
+
test_resource.has_many_service_uri.remote_path.should eql(
|
12
|
+
"/test_resource/1/has_many"
|
13
|
+
)
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|