api_resource 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +55 -0
- data/.rspec +8 -0
- data/.travis.yml +9 -0
- data/Gemfile +3 -36
- data/Gemfile.lock +37 -57
- data/Guardfile +7 -1
- data/{LICENSE.txt → LICENSE} +4 -2
- data/README.md +29 -0
- data/README.rdoc +5 -1
- data/Rakefile +5 -36
- data/api_resource.gemspec +40 -104
- data/lib/api_resource.rb +4 -1
- data/lib/api_resource/associations.rb +30 -14
- data/lib/api_resource/associations/association_proxy.rb +97 -0
- data/lib/api_resource/associations/belongs_to_remote_object_proxy.rb +0 -1
- data/lib/api_resource/associations/has_many_remote_object_proxy.rb +0 -1
- data/lib/api_resource/associations/has_one_remote_object_proxy.rb +7 -4
- data/lib/api_resource/associations/multi_object_proxy.rb +11 -16
- data/lib/api_resource/associations/single_object_proxy.rb +8 -6
- data/lib/api_resource/attributes.rb +29 -41
- data/lib/api_resource/base.rb +31 -102
- data/lib/api_resource/conditions.rb +36 -0
- data/lib/api_resource/conditions/abstract_condition.rb +112 -0
- data/lib/api_resource/conditions/association_condition.rb +18 -0
- data/lib/api_resource/conditions/include_condition.rb +16 -0
- data/lib/api_resource/conditions/multi_object_association_condition.rb +17 -0
- data/lib/api_resource/conditions/scope_condition.rb +11 -0
- data/lib/api_resource/conditions/single_object_association_condition.rb +19 -0
- data/lib/api_resource/connection.rb +18 -12
- data/lib/api_resource/finders.rb +122 -0
- data/lib/api_resource/finders/abstract_finder.rb +89 -0
- data/lib/api_resource/finders/multi_object_association_finder.rb +39 -0
- data/lib/api_resource/finders/resource_finder.rb +33 -0
- data/lib/api_resource/finders/single_object_association_finder.rb +40 -0
- data/lib/api_resource/observing.rb +19 -3
- data/lib/api_resource/scopes.rb +3 -3
- data/lib/api_resource/typecast.rb +85 -0
- data/lib/api_resource/typecasters/array_typecaster.rb +19 -0
- data/lib/api_resource/typecasters/boolean_typecaster.rb +22 -0
- data/lib/api_resource/typecasters/date_typecaster.rb +35 -0
- data/lib/api_resource/typecasters/float_typecaster.rb +22 -0
- data/lib/api_resource/typecasters/integer_typecaster.rb +22 -0
- data/lib/api_resource/typecasters/string_typecaster.rb +19 -0
- data/lib/api_resource/typecasters/time_typecaster.rb +44 -0
- data/lib/api_resource/version.rb +3 -0
- data/spec/lib/api_resource_spec.rb +6 -0
- data/spec/lib/associations/association_scope_spec.rb +1 -1
- data/spec/lib/associations_spec.rb +17 -50
- data/spec/lib/base_spec.rb +22 -0
- data/spec/lib/conditions/abstract_conditions_spec.rb +78 -0
- data/spec/lib/connection_spec.rb +19 -0
- data/spec/lib/finders/multi_object_association_finder_spec.rb +43 -0
- data/spec/lib/finders/resource_finder_spec.rb +89 -0
- data/spec/lib/finders/single_object_association_finder_spec.rb +43 -0
- data/spec/lib/observing_spec.rb +96 -0
- data/spec/lib/typecast_spec.rb +174 -0
- data/spec/lib/typecasters/boolean_typecaster_spec.rb +33 -0
- data/spec/lib/typecasters/date_typecaster_spec.rb +62 -0
- data/spec/lib/typecasters/float_typecaster_spec.rb +39 -0
- data/spec/lib/typecasters/integer_typecaster_spec.rb +40 -0
- data/spec/lib/typecasters/string_typecaster_spec.rb +28 -0
- data/spec/lib/typecasters/time_typecaster_spec.rb +65 -0
- data/spec/spec_helper.rb +0 -1
- metadata +134 -194
- data/VERSION +0 -1
- data/coverage/assets/0.5.3/app.js +0 -88
- 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 +0 -363
- data/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.pack.js +0 -44
- 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 +0 -129
- data/coverage/assets/0.5.3/highlight.pack.js +0 -1
- data/coverage/assets/0.5.3/jquery-1.6.2.min.js +0 -18
- data/coverage/assets/0.5.3/jquery.dataTables.min.js +0 -152
- data/coverage/assets/0.5.3/jquery.timeago.js +0 -141
- data/coverage/assets/0.5.3/jquery.url.js +0 -174
- 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 +0 -295
- data/coverage/assets/0.5.3/stylesheet.css +0 -383
- data/coverage/assets/0.7.1/application.css +0 -1110
- data/coverage/assets/0.7.1/application.js +0 -626
- data/coverage/assets/0.7.1/fancybox/blank.gif +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_close.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_loading.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_nav_left.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_nav_right.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_left.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_main.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_over.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancy_title_right.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox-x.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox-y.png +0 -0
- data/coverage/assets/0.7.1/fancybox/fancybox.png +0 -0
- data/coverage/assets/0.7.1/favicon_green.png +0 -0
- data/coverage/assets/0.7.1/favicon_red.png +0 -0
- data/coverage/assets/0.7.1/favicon_yellow.png +0 -0
- data/coverage/assets/0.7.1/loading.gif +0 -0
- data/coverage/assets/0.7.1/magnify.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/index.html +0 -3564
- data/lib/api_resource/associations/abstract_scope.rb +0 -191
- data/lib/api_resource/associations/association_scope.rb +0 -47
- data/lib/api_resource/associations/resource_scope.rb +0 -21
- data/lib/api_resource/associations/scope.rb +0 -34
- data/spec/lib/associations/resource_scope_spec.rb +0 -24
- data/spec/tmp/api_resource_test_db.sqlite +0 -0
- data/tmp/rspec_guard_result +0 -1
data/lib/api_resource/base.rb
CHANGED
@@ -6,6 +6,30 @@ require 'active_support/string_inquirer'
|
|
6
6
|
module ApiResource
|
7
7
|
|
8
8
|
class Base
|
9
|
+
|
10
|
+
# TODO: There's way too much in this class as it stands, some glaring problems:
|
11
|
+
# => 1) class_attributes for connection options belong in the connection class
|
12
|
+
# => 2) Attributes about serialization belong with the serializers
|
13
|
+
# => 3) Logging seems to be an unmitigated disaster
|
14
|
+
# => 4) All the resource definition should be in its own module with the data mapper implementation
|
15
|
+
# => 5) Random shit about tokens all over the place, clearly does not belong
|
16
|
+
# => 6) Everything related to the pathing should go somewhere by itself
|
17
|
+
# => 7) Everything about saving should go in a persistence module
|
18
|
+
# => 8) Everything about loading data should go in a Finder module
|
19
|
+
# => 9) Delete serializable_hash and start again
|
20
|
+
# => 10) setup_create_call and setup_update_call should modify the results from another method
|
21
|
+
# => 11) nil_attributes probably belongs with the attributes
|
22
|
+
# => 12) Add instrumentation in some key places
|
23
|
+
|
24
|
+
# Other Random TODOs:
|
25
|
+
# => 1) Profile and benchmark this code
|
26
|
+
# => 2) Deal with arrays and hashes as attributes in a more reasonable way
|
27
|
+
# => 3) Finish implementing typecasting (to_api portion)
|
28
|
+
# => 4) Add a reasonable way to load and activate observers, verify that it works
|
29
|
+
# => 5) Hook up the custom_methods module
|
30
|
+
# => 6) Implement an IdentityMap
|
31
|
+
# => 7) Write documentation
|
32
|
+
# => 8) Write Examples
|
9
33
|
|
10
34
|
class_attribute :site, :proxy, :user, :password, :auth_type, :format,
|
11
35
|
:timeout, :open_timeout, :ssl_options, :token, :ttl
|
@@ -297,51 +321,6 @@ module ApiResource
|
|
297
321
|
def create(attributes = {})
|
298
322
|
self.new(attributes).tap{ |resource| resource.save }
|
299
323
|
end
|
300
|
-
|
301
|
-
# This decides which finder method to call.
|
302
|
-
# It accepts arguments of the form "scope", "options={}"
|
303
|
-
# where options can be standard rails options or :expires_in.
|
304
|
-
# If :expires_in is set, it caches it for expires_in seconds.
|
305
|
-
def find(*arguments)
|
306
|
-
|
307
|
-
# make sure we have class data before loading
|
308
|
-
self.load_resource_definition
|
309
|
-
|
310
|
-
scope = arguments.slice!(0)
|
311
|
-
options = arguments.slice!(0) || {}
|
312
|
-
|
313
|
-
expiry = options.delete(:expires_in) || ApiResource::Base.ttl || 0
|
314
|
-
ApiResource.with_ttl(expiry.to_f) do
|
315
|
-
case scope
|
316
|
-
when :all then find_every(options)
|
317
|
-
when :first then find_every(options).first
|
318
|
-
when :last then find_every(options).last
|
319
|
-
when :one then find_one(options)
|
320
|
-
else find_single(scope, options)
|
321
|
-
end
|
322
|
-
end
|
323
|
-
end
|
324
|
-
|
325
|
-
|
326
|
-
# A convenience wrapper for <tt>find(:first, *args)</tt>. You can pass
|
327
|
-
# in all the same arguments to this method as you can to
|
328
|
-
# <tt>find(:first)</tt>.
|
329
|
-
def first(*args)
|
330
|
-
find(:first, *args)
|
331
|
-
end
|
332
|
-
|
333
|
-
# A convenience wrapper for <tt>find(:last, *args)</tt>. You can pass
|
334
|
-
# in all the same arguments to this method as you can to
|
335
|
-
# <tt>find(:last)</tt>.
|
336
|
-
def last(*args)
|
337
|
-
find(:last, *args)
|
338
|
-
end
|
339
|
-
|
340
|
-
# This is an alias for find(:all). You can pass in all the same
|
341
|
-
# arguments to this method as you can to <tt>find(:all)</tt>
|
342
|
-
def all(*args)
|
343
|
-
find(:all, *args)
|
344
|
-
end
|
345
324
|
|
346
325
|
|
347
326
|
# Deletes the resources with the ID in the +id+ parameter.
|
@@ -362,24 +341,6 @@ module ApiResource
|
|
362
341
|
connection.delete(element_path(id, options))
|
363
342
|
end
|
364
343
|
|
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
|
-
|
383
344
|
protected
|
384
345
|
def method_missing(meth, *args, &block)
|
385
346
|
# make one attempt to load remote attrs
|
@@ -390,44 +351,6 @@ module ApiResource
|
|
390
351
|
end
|
391
352
|
|
392
353
|
private
|
393
|
-
# Find every resource
|
394
|
-
def find_every(options)
|
395
|
-
begin
|
396
|
-
case from = options[:from]
|
397
|
-
when Symbol
|
398
|
-
instantiate_collection(get(from, options[:params]))
|
399
|
-
when String
|
400
|
-
path = "#{from}#{query_string(options[:params])}"
|
401
|
-
instantiate_collection(connection.get(path, headers) || [])
|
402
|
-
else
|
403
|
-
prefix_options, query_options = split_options(options[:params])
|
404
|
-
path = collection_path(prefix_options, query_options)
|
405
|
-
instantiate_collection( (connection.get(path, headers) || []))
|
406
|
-
end
|
407
|
-
rescue ApiResource::ResourceNotFound
|
408
|
-
# Swallowing ResourceNotFound exceptions and return nil - as per
|
409
|
-
# ActiveRecord.
|
410
|
-
nil
|
411
|
-
end
|
412
|
-
end
|
413
|
-
|
414
|
-
# Find a single resource from a one-off URL
|
415
|
-
def find_one(options)
|
416
|
-
case from = options[:from]
|
417
|
-
when Symbol
|
418
|
-
instantiate_record(get(from, options[:params]))
|
419
|
-
when String
|
420
|
-
path = "#{from}#{query_string(options[:params])}"
|
421
|
-
instantiate_record(connection.get(path, headers))
|
422
|
-
end
|
423
|
-
end
|
424
|
-
|
425
|
-
# Find a single resource from the default URL
|
426
|
-
def find_single(scope, options)
|
427
|
-
prefix_options, query_options = split_options(options[:params])
|
428
|
-
path = element_path(scope, prefix_options, query_options)
|
429
|
-
instantiate_record(connection.get(path, headers))
|
430
|
-
end
|
431
354
|
|
432
355
|
# Accepts a URI and creates the site URI from that.
|
433
356
|
def create_site_uri_from(site)
|
@@ -582,6 +505,10 @@ module ApiResource
|
|
582
505
|
self.class.include_root_in_json ? {self.class.element_name => self.serializable_hash(options)}.to_json : self.serializable_hash(options).to_json
|
583
506
|
end
|
584
507
|
|
508
|
+
# TODO: this method needs to change seriously to fit in with the
|
509
|
+
# new typecasting scheme, it should call self.outgoing_attributes which
|
510
|
+
# should return the converted versions after calling to_api, that should
|
511
|
+
# be implemented in the attributes module though
|
585
512
|
def serializable_hash(options = {})
|
586
513
|
|
587
514
|
action = options[:action]
|
@@ -683,6 +610,7 @@ module ApiResource
|
|
683
610
|
opts[:include_associations] = opts[:include_associations] ? opts[:include_associations].concat(args) : []
|
684
611
|
opts[:include_extras] ||= []
|
685
612
|
opts[:action] = "create"
|
613
|
+
# TODO: Remove this dependency for saving files
|
686
614
|
body = RestClient::Payload.has_file?(self.attributes) ? self.serializable_hash(opts) : encode(opts)
|
687
615
|
end
|
688
616
|
|
@@ -706,6 +634,7 @@ module ApiResource
|
|
706
634
|
opts[:include_extras] ||= []
|
707
635
|
opts[:action] = "update"
|
708
636
|
opts[:except] = [:id] if self.class.include_all_attributes_on_update
|
637
|
+
# TODO: Remove this dependency for saving files
|
709
638
|
body = RestClient::Payload.has_file?(self.attributes) ? self.serializable_hash(opts) : encode(opts)
|
710
639
|
end
|
711
640
|
|
@@ -724,7 +653,7 @@ module ApiResource
|
|
724
653
|
include AssociationActivation
|
725
654
|
self.activate_associations
|
726
655
|
|
727
|
-
include Scopes, Callbacks, Attributes, ModelErrors
|
656
|
+
include Scopes, Callbacks, Observing, Attributes, ModelErrors, Conditions, Finders
|
728
657
|
|
729
658
|
end
|
730
659
|
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ApiResource
|
2
|
+
|
3
|
+
module Conditions
|
4
|
+
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
autoload :AbstractCondition
|
9
|
+
autoload :AssociationCondition
|
10
|
+
autoload :SingleObjectAssociationCondition
|
11
|
+
autoload :MultiObjectAssociationCondition
|
12
|
+
autoload :IncludeCondition
|
13
|
+
autoload :ScopeCondition
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
|
17
|
+
def includes(*args)
|
18
|
+
|
19
|
+
# everything in args must be an association
|
20
|
+
args.each do |arg|
|
21
|
+
unless self.association?(arg)
|
22
|
+
raise ArgumentError, "Unknown association #{arg} to eager load"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Everything looks good so just create the scope
|
27
|
+
ApiResource::Conditions::IncludeCondition.new(self, args)
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module ApiResource
|
2
|
+
|
3
|
+
module Conditions
|
4
|
+
|
5
|
+
class AbstractCondition
|
6
|
+
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_reader :conditions, :klass, :included_objects, :internal_object, :association,
|
10
|
+
:remote_path
|
11
|
+
|
12
|
+
# TODO: add the other load forcing methods here for collections
|
13
|
+
delegate :[], :[]=, :<<, :first, :second, :last, :blank?, :nil?, :include?, :push, :pop,
|
14
|
+
:+, :concat, :flatten, :flatten!, :compact, :compact!, :empty?, :fetch, :map,
|
15
|
+
:reject, :reject!, :reverse, :select, :select!, :size, :sort, :sort!, :uniq, :uniq!,
|
16
|
+
:to_a, :sample, :slice, :slice!, :count, :present?, :to => :internal_object
|
17
|
+
|
18
|
+
# need to figure out what to do with args in the subclass,
|
19
|
+
# parent is the set of scopes we have right now
|
20
|
+
def initialize(args, klass)
|
21
|
+
@klass = klass
|
22
|
+
|
23
|
+
@conditions = args.with_indifferent_access
|
24
|
+
end
|
25
|
+
|
26
|
+
def each(&block)
|
27
|
+
self.internal_object.each(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def blank_conditions?
|
31
|
+
self.conditions.blank?
|
32
|
+
end
|
33
|
+
|
34
|
+
def eager_load?
|
35
|
+
self.included_objects.present?
|
36
|
+
end
|
37
|
+
|
38
|
+
def included_objects
|
39
|
+
Array.wrap(@included_objects)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_query
|
43
|
+
CGI.unescape(to_query_safe_hash(self.to_hash).to_query)
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_hash
|
47
|
+
self.conditions
|
48
|
+
end
|
49
|
+
|
50
|
+
def internal_object
|
51
|
+
@internal_object ||= begin
|
52
|
+
@loaded = true
|
53
|
+
self.instantiate_finder.find
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
alias_method :find, :internal_object
|
58
|
+
alias_method :all, :internal_object
|
59
|
+
|
60
|
+
def loaded?
|
61
|
+
@loaded == true
|
62
|
+
end
|
63
|
+
|
64
|
+
def reload
|
65
|
+
if instance_variable_defined?(:@internal_object)
|
66
|
+
remove_instance_variable(:@internal_object)
|
67
|
+
end
|
68
|
+
@loaded = false
|
69
|
+
end
|
70
|
+
|
71
|
+
def method_missing(sym, *args, &block)
|
72
|
+
result = @klass.send(sym, *args, &block)
|
73
|
+
|
74
|
+
if result.is_a?(ApiResource::Conditions::AbstractCondition)
|
75
|
+
return self.dup.merge!(result)
|
76
|
+
else
|
77
|
+
return result
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def expires_in(time)
|
82
|
+
ApiResource::Decorators::CachingDecorator.new(self, time)
|
83
|
+
end
|
84
|
+
|
85
|
+
# TODO: Remove the bang, this doesn't modify anything
|
86
|
+
def merge!(cond)
|
87
|
+
@included_objects = (@included_objects || []).concat(cond.included_objects || []).uniq
|
88
|
+
@conditions = @conditions.merge(cond.to_hash)
|
89
|
+
@association = cond.association || self.association
|
90
|
+
@remote_path = self.remote_path ? self.remote_path : cond.remote_path
|
91
|
+
return self
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def instantiate_finder
|
97
|
+
ApiResource::Finders::ResourceFinder.new(self.klass, self)
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_query_safe_hash(hash)
|
101
|
+
hash.each_pair do |k,v|
|
102
|
+
hash[k] = to_query_safe_hash(v) if v.is_a?(Hash)
|
103
|
+
hash[k] = true if v == {}
|
104
|
+
end
|
105
|
+
return hash
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ApiResource
|
2
|
+
|
3
|
+
module Conditions
|
4
|
+
|
5
|
+
class MultiObjectAssociationCondition < AssociationCondition
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def instantiate_finder
|
10
|
+
ApiResource::Finders::MultiObjectAssociationFinder.new(self.klass, self)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ApiResource
|
2
|
+
|
3
|
+
module Conditions
|
4
|
+
|
5
|
+
# Special class for passing into a finder object
|
6
|
+
# just defines all the proper methods
|
7
|
+
class SingleObjectAssociationCondition < AssociationCondition
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def instantiate_finder
|
12
|
+
ApiResource::Finders::SingleObjectAssociationFinder.new(self.klass, self)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -70,22 +70,28 @@ module ApiResource
|
|
70
70
|
request(:head, path, build_request_headers(headers, :head, self.site.merge(path)))
|
71
71
|
end
|
72
72
|
|
73
|
-
|
73
|
+
# make a put request
|
74
74
|
def put(path, body = {}, headers = self.headers)
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
75
|
+
format.decode(
|
76
|
+
request(
|
77
|
+
:put,
|
78
|
+
path,
|
79
|
+
body,
|
80
|
+
build_request_headers(headers, :put, self.site.merge(path))
|
81
|
+
)
|
82
|
+
)
|
81
83
|
end
|
82
84
|
|
85
|
+
# make a post request
|
83
86
|
def post(path, body = {}, headers = self.headers)
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
87
|
+
format.decode(
|
88
|
+
request(
|
89
|
+
:post,
|
90
|
+
path,
|
91
|
+
body,
|
92
|
+
build_request_headers(headers, :post, self.site.merge(path))
|
93
|
+
)
|
94
|
+
)
|
89
95
|
end
|
90
96
|
|
91
97
|
protected
|