api_resource 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/Gemfile +37 -0
  2. data/Gemfile.lock +190 -0
  3. data/Guardfile +27 -0
  4. data/Rakefile +49 -0
  5. data/VERSION +1 -0
  6. data/api_resource.gemspec +111 -0
  7. data/coverage/assets/0.5.3/app.js +88 -0
  8. data/coverage/assets/0.5.3/fancybox/blank.gif +0 -0
  9. data/coverage/assets/0.5.3/fancybox/fancy_close.png +0 -0
  10. data/coverage/assets/0.5.3/fancybox/fancy_loading.png +0 -0
  11. data/coverage/assets/0.5.3/fancybox/fancy_nav_left.png +0 -0
  12. data/coverage/assets/0.5.3/fancybox/fancy_nav_right.png +0 -0
  13. data/coverage/assets/0.5.3/fancybox/fancy_shadow_e.png +0 -0
  14. data/coverage/assets/0.5.3/fancybox/fancy_shadow_n.png +0 -0
  15. data/coverage/assets/0.5.3/fancybox/fancy_shadow_ne.png +0 -0
  16. data/coverage/assets/0.5.3/fancybox/fancy_shadow_nw.png +0 -0
  17. data/coverage/assets/0.5.3/fancybox/fancy_shadow_s.png +0 -0
  18. data/coverage/assets/0.5.3/fancybox/fancy_shadow_se.png +0 -0
  19. data/coverage/assets/0.5.3/fancybox/fancy_shadow_sw.png +0 -0
  20. data/coverage/assets/0.5.3/fancybox/fancy_shadow_w.png +0 -0
  21. data/coverage/assets/0.5.3/fancybox/fancy_title_left.png +0 -0
  22. data/coverage/assets/0.5.3/fancybox/fancy_title_main.png +0 -0
  23. data/coverage/assets/0.5.3/fancybox/fancy_title_over.png +0 -0
  24. data/coverage/assets/0.5.3/fancybox/fancy_title_right.png +0 -0
  25. data/coverage/assets/0.5.3/fancybox/fancybox-x.png +0 -0
  26. data/coverage/assets/0.5.3/fancybox/fancybox-y.png +0 -0
  27. data/coverage/assets/0.5.3/fancybox/fancybox.png +0 -0
  28. data/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.css +363 -0
  29. data/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.pack.js +44 -0
  30. data/coverage/assets/0.5.3/favicon_green.png +0 -0
  31. data/coverage/assets/0.5.3/favicon_red.png +0 -0
  32. data/coverage/assets/0.5.3/favicon_yellow.png +0 -0
  33. data/coverage/assets/0.5.3/highlight.css +129 -0
  34. data/coverage/assets/0.5.3/highlight.pack.js +1 -0
  35. data/coverage/assets/0.5.3/jquery-1.6.2.min.js +18 -0
  36. data/coverage/assets/0.5.3/jquery.dataTables.min.js +152 -0
  37. data/coverage/assets/0.5.3/jquery.timeago.js +141 -0
  38. data/coverage/assets/0.5.3/jquery.url.js +174 -0
  39. data/coverage/assets/0.5.3/loading.gif +0 -0
  40. data/coverage/assets/0.5.3/magnify.png +0 -0
  41. data/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  42. data/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  43. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  44. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  45. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  46. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  47. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  48. data/coverage/assets/0.5.3/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  49. data/coverage/assets/0.5.3/smoothness/images/ui-icons_222222_256x240.png +0 -0
  50. data/coverage/assets/0.5.3/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  51. data/coverage/assets/0.5.3/smoothness/images/ui-icons_454545_256x240.png +0 -0
  52. data/coverage/assets/0.5.3/smoothness/images/ui-icons_888888_256x240.png +0 -0
  53. data/coverage/assets/0.5.3/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  54. data/coverage/assets/0.5.3/smoothness/jquery-ui-1.8.4.custom.css +295 -0
  55. data/coverage/assets/0.5.3/stylesheet.css +383 -0
  56. data/coverage/index.html +3573 -0
  57. data/lib/api_resource.rb +130 -0
  58. data/lib/api_resource/association_activation.rb +19 -0
  59. data/lib/api_resource/associations.rb +218 -0
  60. data/lib/api_resource/associations/association_proxy.rb +116 -0
  61. data/lib/api_resource/associations/belongs_to_remote_object_proxy.rb +16 -0
  62. data/lib/api_resource/associations/dynamic_resource_scope.rb +23 -0
  63. data/lib/api_resource/associations/generic_scope.rb +68 -0
  64. data/lib/api_resource/associations/has_many_remote_object_proxy.rb +16 -0
  65. data/lib/api_resource/associations/has_many_through_remote_object_proxy.rb +13 -0
  66. data/lib/api_resource/associations/has_one_remote_object_proxy.rb +24 -0
  67. data/lib/api_resource/associations/multi_argument_resource_scope.rb +15 -0
  68. data/lib/api_resource/associations/multi_object_proxy.rb +84 -0
  69. data/lib/api_resource/associations/related_object_hash.rb +12 -0
  70. data/lib/api_resource/associations/relation_scope.rb +25 -0
  71. data/lib/api_resource/associations/resource_scope.rb +32 -0
  72. data/lib/api_resource/associations/scope.rb +132 -0
  73. data/lib/api_resource/associations/single_object_proxy.rb +82 -0
  74. data/lib/api_resource/attributes.rb +243 -0
  75. data/lib/api_resource/base.rb +717 -0
  76. data/lib/api_resource/callbacks.rb +45 -0
  77. data/lib/api_resource/connection.rb +195 -0
  78. data/lib/api_resource/core_extensions.rb +7 -0
  79. data/lib/api_resource/custom_methods.rb +117 -0
  80. data/lib/api_resource/decorators.rb +6 -0
  81. data/lib/api_resource/decorators/caching_decorator.rb +20 -0
  82. data/lib/api_resource/exceptions.rb +99 -0
  83. data/lib/api_resource/formats.rb +22 -0
  84. data/lib/api_resource/formats/json_format.rb +25 -0
  85. data/lib/api_resource/formats/xml_format.rb +36 -0
  86. data/lib/api_resource/local.rb +12 -0
  87. data/lib/api_resource/log_subscriber.rb +15 -0
  88. data/lib/api_resource/mocks.rb +285 -0
  89. data/lib/api_resource/model_errors.rb +82 -0
  90. data/lib/api_resource/observing.rb +27 -0
  91. data/lib/api_resource/railtie.rb +24 -0
  92. data/lib/api_resource/scopes.rb +48 -0
  93. data/nohup.out +63 -0
  94. data/spec/lib/api_resource_spec.rb +43 -0
  95. data/spec/lib/associations_spec.rb +751 -0
  96. data/spec/lib/attributes_spec.rb +191 -0
  97. data/spec/lib/base_spec.rb +655 -0
  98. data/spec/lib/callbacks_spec.rb +68 -0
  99. data/spec/lib/connection_spec.rb +137 -0
  100. data/spec/lib/local_spec.rb +20 -0
  101. data/spec/lib/mocks_spec.rb +74 -0
  102. data/spec/lib/model_errors_spec.rb +29 -0
  103. data/spec/lib/prefixes_spec.rb +107 -0
  104. data/spec/spec_helper.rb +82 -0
  105. data/spec/support/mocks/association_mocks.rb +63 -0
  106. data/spec/support/mocks/error_resource_mocks.rb +21 -0
  107. data/spec/support/mocks/prefix_model_mocks.rb +5 -0
  108. data/spec/support/mocks/test_resource_mocks.rb +44 -0
  109. data/spec/support/requests/association_requests.rb +31 -0
  110. data/spec/support/requests/error_resource_requests.rb +25 -0
  111. data/spec/support/requests/prefix_model_requests.rb +7 -0
  112. data/spec/support/requests/test_resource_requests.rb +38 -0
  113. data/spec/support/test_resource.rb +72 -0
  114. data/spec/tmp/DIR +0 -0
  115. data/spec/tmp/api_resource_test_db.sqlite +0 -0
  116. metadata +119 -3
@@ -0,0 +1,27 @@
1
+ module ApiResource
2
+
3
+ module Observing
4
+
5
+ extend ActiveSupport::Concern
6
+ include ActiveModel::Observing
7
+
8
+ # Redefine these methods to
9
+ included do
10
+ %w( create save update destroy ).each do |method|
11
+ alias_method_chain method, :observers
12
+ end
13
+
14
+ %w( create save update destroy ).each do |method|
15
+ module_eval <<-EOE, __FILE__, __LINE__ + 1
16
+ def #{method}_with_observers(*args)
17
+ notify_observers(:before_#method)
18
+ if result = #{method}_without_observers(*args)
19
+ notify_observers(:after_#{method})
20
+ end
21
+ return result
22
+ end
23
+ EOE
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ require 'api_resource'
2
+ require 'rails'
3
+
4
+ module ApiResource
5
+
6
+ class Railtie < ::Rails::Railtie
7
+
8
+ config.api_resource = ActiveSupport::OrderedOptions.new
9
+
10
+ initializer "api_resource.set_configs" do |app|
11
+ app.config.api_resource.each do |k,v|
12
+ ApiResource::Base.send "#{k}=", v
13
+ end
14
+ end
15
+
16
+ initializer "api_resource.activate_associations" do
17
+ ActiveSupport.on_load(:active_record) do
18
+ ApiResource::Associations.activate_active_record
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,48 @@
1
+ module ApiResource
2
+ module Scopes
3
+ extend ActiveSupport::Concern
4
+
5
+
6
+ module ClassMethods
7
+ def scopes
8
+ return self.related_objects[:scopes]
9
+ end
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
+ def scope?(name)
26
+ self.related_objects[:scopes][name.to_sym].present?
27
+ end
28
+
29
+ def scope_attributes(name)
30
+ raise "No such scope #{name}" unless self.scope?(name)
31
+ self.related_objects[:scopes][name.to_sym]
32
+ end
33
+ end
34
+
35
+ def scopes
36
+ return self.class.scopes
37
+ end
38
+
39
+ def scope?(name)
40
+ return self.class.scope?(name)
41
+ end
42
+
43
+ def scope_attributes(name)
44
+ return self.class.scope_attributes(name)
45
+ end
46
+
47
+ end
48
+ end
data/nohup.out ADDED
@@ -0,0 +1,63 @@
1
+ /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/choice-0.1.4/lib/choice/parser.rb:108: warning: Hash#index is deprecated; use Hash#key
2
+ cd: 1: can't cd to /vol/www/releases/api_resource/api_resource
3
+ /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/config.rb:25:in `to_s': Error calling git config, is a recent version of git installed? Command: "cd /vol/www/releases/api_resource/api_resource && git config cijoe.user", Error: "", Status: #<Process::Status: pid 18804 exit 2> (RuntimeError)
4
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/server.rb:96:in `project_path='
5
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/sinatra-1.2.7/lib/sinatra/base.rb:964:in `set'
6
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/server.rb:86:in `start'
7
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/bin/cijoe:50:in `<top (required)>'
8
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/bin/cijoe:19:in `load'
9
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/bin/cijoe:19:in `<main>'
10
+ /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/choice-0.1.4/lib/choice/parser.rb:108: warning: Hash#index is deprecated; use Hash#key
11
+ cd: 1: can't cd to /vol/www/releases/api_resource/api_resource
12
+ /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/config.rb:25:in `to_s': Error calling git config, is a recent version of git installed? Command: "cd /vol/www/releases/api_resource/api_resource && git config cijoe.user", Error: "", Status: #<Process::Status: pid 19377 exit 2> (RuntimeError)
13
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/server.rb:96:in `project_path='
14
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/sinatra-1.2.7/lib/sinatra/base.rb:964:in `set'
15
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/server.rb:86:in `start'
16
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/bin/cijoe:50:in `<top (required)>'
17
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/bin/cijoe:19:in `load'
18
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/bin/cijoe:19:in `<main>'
19
+ /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/choice-0.1.4/lib/choice/parser.rb:108: warning: Hash#index is deprecated; use Hash#key
20
+ cd: 1: can't cd to /vol/www/releases/api_resource/api_resource
21
+ /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/config.rb:25:in `to_s': Error calling git config, is a recent version of git installed? Command: "cd /vol/www/releases/api_resource/api_resource && git config cijoe.user", Error: "", Status: #<Process::Status: pid 19463 exit 2> (RuntimeError)
22
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/server.rb:96:in `project_path='
23
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/sinatra-1.2.7/lib/sinatra/base.rb:964:in `set'
24
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/server.rb:86:in `start'
25
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/bin/cijoe:50:in `<top (required)>'
26
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/bin/cijoe:19:in `load'
27
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/bin/cijoe:19:in `<main>'
28
+ /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/choice-0.1.4/lib/choice/parser.rb:108: warning: Hash#index is deprecated; use Hash#key
29
+ cd: 1: can't cd to /vol/www/releases/api_resource/api_resource
30
+ /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/config.rb:25:in `to_s': Error calling git config, is a recent version of git installed? Command: "cd /vol/www/releases/api_resource/api_resource && git config cijoe.user", Error: "", Status: #<Process::Status: pid 19730 exit 2> (RuntimeError)
31
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/server.rb:96:in `project_path='
32
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/sinatra-1.2.7/lib/sinatra/base.rb:964:in `set'
33
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/server.rb:86:in `start'
34
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/bin/cijoe:50:in `<top (required)>'
35
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/bin/cijoe:19:in `load'
36
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/bin/cijoe:19:in `<main>'
37
+ /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/choice-0.1.4/lib/choice/parser.rb:108: warning: Hash#index is deprecated; use Hash#key
38
+ cd: 1: can't cd to /vol/www/releases/api_resource/api_resource
39
+ /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/config.rb:25:in `to_s': Error calling git config, is a recent version of git installed? Command: "cd /vol/www/releases/api_resource/api_resource && git config cijoe.user", Error: "", Status: #<Process::Status: pid 19738 exit 2> (RuntimeError)
40
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/server.rb:96:in `project_path='
41
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/sinatra-1.2.7/lib/sinatra/base.rb:964:in `set'
42
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/lib/cijoe/server.rb:86:in `start'
43
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/gems/cijoe-0.9.2/bin/cijoe:50:in `<top (required)>'
44
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/bin/cijoe:19:in `load'
45
+ from /home/bb/.rvm/gems/ruby-1.9.2-p290/bin/cijoe:19:in `<main>'
46
+ cd: 1: can't cd to /vol/www/releases/api_resource/api_resource
47
+ /usr/lib/ruby/gems/1.8/gems/cijoe-0.9.2/bin/../lib/cijoe/config.rb:25:in `to_s': Error calling git config, is a recent version of git installed? Command: "cd /vol/www/releases/api_resource/api_resource && git config cijoe.user", Error: "", Status: #<Process::Status: pid=19748,exited(2)> (RuntimeError)
48
+ from /usr/lib/ruby/gems/1.8/gems/cijoe-0.9.2/bin/../lib/cijoe/server.rb:96:in `project_path='
49
+ from /usr/lib/ruby/gems/1.8/gems/sinatra-1.2.7/lib/sinatra/base.rb:964:in `__send__'
50
+ from /usr/lib/ruby/gems/1.8/gems/sinatra-1.2.7/lib/sinatra/base.rb:964:in `set'
51
+ from /usr/lib/ruby/gems/1.8/gems/cijoe-0.9.2/bin/../lib/cijoe/server.rb:86:in `start'
52
+ from /usr/lib/ruby/gems/1.8/gems/cijoe-0.9.2/bin/cijoe:50
53
+ from /usr/bin/cijoe:19:in `load'
54
+ from /usr/bin/cijoe:19
55
+ cd: 1: can't cd to /vol/www/releases/api_resource/api_resource
56
+ /usr/lib/ruby/gems/1.8/gems/cijoe-0.9.2/bin/../lib/cijoe/config.rb:25:in `to_s': Error calling git config, is a recent version of git installed? Command: "cd /vol/www/releases/api_resource/api_resource && git config cijoe.user", Error: "", Status: #<Process::Status: pid=19909,exited(2)> (RuntimeError)
57
+ from /usr/lib/ruby/gems/1.8/gems/cijoe-0.9.2/bin/../lib/cijoe/server.rb:96:in `project_path='
58
+ from /usr/lib/ruby/gems/1.8/gems/sinatra-1.2.7/lib/sinatra/base.rb:964:in `__send__'
59
+ from /usr/lib/ruby/gems/1.8/gems/sinatra-1.2.7/lib/sinatra/base.rb:964:in `set'
60
+ from /usr/lib/ruby/gems/1.8/gems/cijoe-0.9.2/bin/../lib/cijoe/server.rb:86:in `start'
61
+ from /usr/lib/ruby/gems/1.8/gems/cijoe-0.9.2/bin/cijoe:50
62
+ from /usr/bin/cijoe:19:in `load'
63
+ from /usr/bin/cijoe:19
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApiResource do
4
+
5
+ context ".cache" do
6
+
7
+ around(:each) do |example|
8
+ begin
9
+ old_rails = Object.send(:remove_const, :Rails)
10
+ example.run
11
+ ensure
12
+ Rails = old_rails
13
+ ApiResource
14
+ end
15
+ end
16
+
17
+ it "should be a Rails cache if it's initialized" do
18
+ cache_stub = stub()
19
+ Rails = mock(:cache => cache_stub)
20
+ ApiResource.cache(true).should be cache_stub
21
+ end
22
+
23
+ it "should default to an instance of memory cache" do
24
+ defined?(Rails).should be_blank
25
+ ApiResource.cache(true).should be_a(
26
+ ActiveSupport::Cache::MemoryStore
27
+ )
28
+ end
29
+ end
30
+
31
+ context ".with_ttl" do
32
+
33
+ it "should temporarily set ttl for a block" do
34
+ old_ttl = ApiResource.ttl
35
+ ApiResource.with_ttl(10) do
36
+ ApiResource.ttl.should eql(10)
37
+ end
38
+ ApiResource.ttl.should eql(old_ttl)
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,751 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ include ApiResource
4
+
5
+ describe "Associations" do
6
+
7
+ after(:all) do
8
+ TestResource.reload_class_attributes
9
+ end
10
+
11
+ context "creating and testing for associations of various types" do
12
+
13
+ it "should be able to give a list of all associations" do
14
+ AllAssociations = Class.new(ApiResource::Base)
15
+ AllAssociations.class_eval do
16
+ has_many :has_many_objects
17
+ belongs_to :belongs_to_object
18
+ has_one :has_one_object
19
+ end
20
+ AllAssociations.association_names.sort.should eql [:has_many_objects, :belongs_to_object, :has_one_object].sort
21
+ AllAssociations.new.association_names.sort.should eql [:has_many_objects, :belongs_to_object, :has_one_object].sort
22
+ end
23
+
24
+ it "should be be able to define an asociation using a method named after that association type" do
25
+ TestResource.has_many :has_many_objects
26
+ TestResource.has_many?(:has_many_objects).should be_true
27
+ end
28
+
29
+ it "should be able to define associations with different class names" do
30
+ TestResource.has_many :test_name, :class_name => :has_many_objects
31
+ TestResource.has_many?(:test_name).should be_true
32
+ TestResource.has_many_class_name(:test_name).should eql("HasManyObject")
33
+ end
34
+
35
+ it "should be able to define multiple associations at the same time" do
36
+ TestResource.has_many :has_many_objects, :other_has_many_objects
37
+ TestResource.has_many?(:has_many_objects).should be_true
38
+ TestResource.has_many?(:other_has_many_objects).should be_true
39
+ end
40
+
41
+ it "should be able to tell if something is an association via the association? method" do
42
+ TestResource.belongs_to :belongs_to_object
43
+ TestResource.association?(:belongs_to_object).should be_true
44
+ end
45
+
46
+ it "should be able to get the class name of an association via the association_class_name method" do
47
+ TestResource.belongs_to :belongs_to_object
48
+ TestResource.association_class_name(:belongs_to_object).should eql("BelongsToObject")
49
+ TestResource.belongs_to :strange_name, :class_name => :belongs_to_object
50
+ TestResource.association_class_name(:strange_name).should eql("BelongsToObject")
51
+ end
52
+
53
+ it "should only define relationships for the given class - they should not cascade" do
54
+ TestResource.belongs_to :belongs_to_object
55
+ AnotherTestResource.association?(:belongs_to_object).should_not be_true
56
+ end
57
+
58
+ it "should have its relationship cascade when sub-classed after the relationship is defined" do
59
+ TestResource.belongs_to :belongs_to_object
60
+ class ChildTestResource2 < TestResource; end
61
+ ChildTestResource2.association?(:belongs_to_object).should be true
62
+ end
63
+
64
+ context "Determining associated classes with a namespace" do
65
+
66
+ it "should be able to find classes for associations that exist in the same module without a namespace" do
67
+ TestMod::TestClass.belongs_to :test_association
68
+ TestMod::TestClass.association_class_name(:test_association).should eql("TestMod::TestAssociation")
69
+ end
70
+
71
+ it "should be return a regular class name for a class defined at the root level" do
72
+ TestMod::TestClass.belongs_to :belongs_to_object
73
+ TestMod::TestClass.association_class_name(:belongs_to_object).should eql("BelongsToObject")
74
+ end
75
+
76
+ it "should work for a class name specified with a namespace module" do
77
+ TestMod::TestClass.belongs_to :nonsense, :class_name => "TestMod::TestAssociation"
78
+ TestMod::TestClass.association_class_name(:nonsense).should eql("TestMod::TestAssociation")
79
+ end
80
+
81
+ it "should work for nested module as well" do
82
+ TestMod::InnerMod::InnerClass.belongs_to :test_association
83
+ TestMod::InnerMod::InnerClass.association_class_name(:test_association).should eql("TestMod::TestAssociation")
84
+ end
85
+
86
+ it "should prefer to find classes within similar modules to ones in the root namespace" do
87
+ TestMod::InnerMod::InnerClass.belongs_to :test_resource
88
+ TestMod::InnerMod::InnerClass.association_class_name(:test_resource).should eql("TestMod::TestResource")
89
+ end
90
+
91
+ it "should be able to override into the root namespace by prefixing with ::" do
92
+ TestMod::InnerMod::InnerClass.belongs_to :test_resource, :class_name => "::TestResource"
93
+ TestMod::InnerMod::InnerClass.association_class_name(:test_resource).should eql("::TestResource")
94
+ end
95
+
96
+ end
97
+
98
+
99
+ end
100
+
101
+ context "Remote Definitions" do
102
+
103
+ before(:all) do
104
+ TestResource.reload_class_attributes
105
+ end
106
+
107
+ it "should be able define an association remotely" do
108
+ TestResource.belongs_to?(:belongs_to_object).should be true
109
+ TestResource.new.belongs_to_object.klass.should eql BelongsToObject
110
+ end
111
+
112
+ it "should be able define an association remotely" do
113
+ TestResource.belongs_to?(:custom_name).should be true
114
+ TestResource.new.custom_name.klass.should eql BelongsToObject
115
+ end
116
+
117
+ end
118
+
119
+
120
+ context "creating and testing for scopes" do
121
+
122
+ it "should be able to define scopes which require class names" do
123
+ lambda {
124
+ TestResource.scope :test_scope
125
+ }.should raise_error
126
+ TestResource.scope :test_scope, {:has_many_objects => "test"}
127
+ end
128
+
129
+ it "should be able to test if a scope exists" do
130
+ TestResource.scope :test_scope, {:item => "test"}
131
+ TestResource.scope?(:test_scope).should be_true
132
+ TestResource.scope_attributes(:test_scope).should eql({"item" => "test"})
133
+ end
134
+
135
+ it "should not propagate scopes from one class to another" do
136
+
137
+ Scope1Class = Class.new(ApiResource::Base) do
138
+ scope :one, {:item => "test"}
139
+ end
140
+
141
+ Scope2Class = Class.new(ApiResource::Base) do
142
+ scope :two, {:abc => "def"}
143
+ end
144
+
145
+ Scope1Class.scope?(:one).should be true
146
+ Scope1Class.scope?(:two).should be false
147
+
148
+ Scope2Class.scope?(:one).should be false
149
+ Scope2Class.scope?(:two).should be true
150
+
151
+ end
152
+
153
+ end
154
+
155
+ context "testing for scopes and associations on an instance" do
156
+
157
+ it "should be able to define associations on a class and test for them on an instance" do
158
+ TestResource.has_many :has_many_objects, :class_name => :other_has_many_objects
159
+ tst = TestResource.new
160
+ tst.has_many?(:has_many_objects).should be_true
161
+ tst.has_many_class_name(:has_many_objects).should eql("OtherHasManyObject")
162
+ end
163
+
164
+ it "should be able to define scopes on a class and test for them on an instance" do
165
+ TestResource.scope :has_many_objects, {:item => "test"}
166
+ tst = TestResource.new
167
+ tst.scope?(:has_many_objects).should be_true
168
+ tst.scope_attributes(:has_many_objects).should eql({"item" => "test"})
169
+ end
170
+
171
+ end
172
+
173
+ describe "Single Object Associations" do
174
+
175
+ before(:all) do
176
+ TestResource.reload_class_attributes
177
+ end
178
+
179
+ after(:all) do
180
+ TestResource.reload_class_attributes
181
+ end
182
+
183
+ it "should return nil if its internal object is nil" do
184
+ ap = Associations::SingleObjectProxy.new("TestResource", {})
185
+ ap.instance_variable_set(:@internal_object, nil)
186
+ ap.blank?.should be_true
187
+ end
188
+
189
+ it "should not throw an error on serializable hash if its internal object is nil" do
190
+ ap = Associations::SingleObjectProxy.new("TestResource", {})
191
+ ap.instance_variable_set(:@internal_object, nil)
192
+ lambda {ap.serializable_hash}.should_not raise_error
193
+ end
194
+
195
+ it "should be able to create a SingleObjectProxy around a blank hash" do
196
+ ap = Associations::SingleObjectProxy.new("TestResource", {})
197
+ ap.remote_path.should be_blank
198
+ end
199
+
200
+ it "should be able to extract a service uri from the contents hash" do
201
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => "/path"})
202
+ ap.remote_path.should eql("/path")
203
+ end
204
+
205
+ it "should be able to recognize the attributes of an object and not make them scopes" do
206
+ TestResource.define_attributes :test
207
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => "/path", :test => "testval"})
208
+ ap.scope?("test").should be_false
209
+ ap.remote_path.should eql("/path")
210
+ end
211
+
212
+ it "should make all attributes except the service uri into scopes given the scopes_only option" do
213
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => "/path", :test_scope => {"testval" => true}, :scopes_only => true})
214
+ ap.scope?("test_scope").should be_true
215
+ ap.remote_path.should eql("/path")
216
+ end
217
+
218
+ it "should pass the attributes that are not scopes and make them attributes of the object" do
219
+ TestResource.define_attributes :test
220
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => "/path", :test => "testval"})
221
+ ap.internal_object.attributes.keys.should include("test")
222
+ end
223
+ end
224
+
225
+ describe "Multi Object Associations" do
226
+
227
+ before(:all) do
228
+ TestResource.related_objects[:scopes].clear
229
+ end
230
+
231
+ after(:each) do
232
+ Associations::MultiObjectProxy.remote_path_element = :service_uri
233
+ end
234
+
235
+ describe "Loading settings" do
236
+
237
+ context "Loading array contents" do
238
+
239
+ it "should be able to load a blank array" do
240
+ ap = Associations::MultiObjectProxy.new("TestResource",[])
241
+ ap.remote_path.should be_nil
242
+ ap.scopes.keys.should eql([])
243
+ end
244
+
245
+ it "should be able to recognize a settings hash if it has a service_uri" do
246
+ ap = Associations::MultiObjectProxy.new("TestResource",[{:service_uri => "/route"}])
247
+ ap.remote_path.should eql("/route")
248
+ end
249
+
250
+ it "should be able to recognize a settings hash if it has a 'service_uri' with another preset name" do
251
+ Associations::MultiObjectProxy.remote_path_element = :the_element
252
+ ap = Associations::MultiObjectProxy.new("TestResource",[{:the_element => "/route"}])
253
+ ap.remote_path.should eql("/route")
254
+ end
255
+
256
+ end
257
+
258
+ context "Loading hash contents" do
259
+ it "should not be able to load a hash without a 'service_uri'" do
260
+ lambda {
261
+ Associations::MultiObjectProxy.new("TestResource", {:hi => 3})
262
+ }.should raise_error
263
+ end
264
+
265
+ it "should be able to recognize settings from a hash" do
266
+ ap = Associations::MultiObjectProxy.new("TestResource", {:service_uri => "/route"})
267
+ ap.remote_path.should eql("/route")
268
+ end
269
+
270
+ it "should recognize settings with differing 'service_uri' names" do
271
+ Associations::MultiObjectProxy.remote_path_element = :the_element
272
+ ap = Associations::MultiObjectProxy.new("TestResource",{:the_element => "/route"})
273
+ ap.remote_path.should eql("/route")
274
+ end
275
+ end
276
+
277
+ context "Defining scopes" do
278
+
279
+ it "should define scopes based on the other keys in a settings hash" do
280
+ ap = Associations::MultiObjectProxy.new("TestResource", [{:service_uri => "/route", :scope1 => {"scope1" => true}, :scope2 => {"scope2" => true}}])
281
+ [:scope1, :scope2].each{|s| ap.scopes.include?(s).should be_true }
282
+ end
283
+
284
+ it "should identify known scopes based on the scopes defined on the object it is a proxy to" do
285
+ TestResource.scope :class_scope, "class_scope" => "true"
286
+ ap = Associations::MultiObjectProxy.new("TestResource", [{:service_uri => "/route", :scope1 => {"scope1" => true}, :scope2 => {"scope2" => true}}])
287
+ [:scope1, :scope2, :class_scope].each{|s| ap.scopes.include?(s).should be_true}
288
+ end
289
+
290
+ it "scopes in the response should shadow class defined scopes" do
291
+ TestResource.scope :scope1, "scope1" => "true"
292
+ ap = Associations::MultiObjectProxy.new("TestResource", [{:service_uri => "/route", :scope1 => {"scope1" => true}, :scope2 => {"scope2" => true}}])
293
+ ap.scopes[:scope1].should eql({"scope1" => true})
294
+ end
295
+ end
296
+
297
+ end
298
+
299
+ describe "Selecting scopes" do
300
+
301
+ before(:all) do
302
+ ScopeResource.class_eval do
303
+ scope :no_arg, {}
304
+ scope :one_arg, {:id => :req}
305
+ scope :one_array_arg, {:ids => :req}
306
+ scope :two_args, {:page => :req, :per_page => :req}
307
+ scope :opt_args, {:arg1 => :opt}
308
+ scope :var_args, {:ids => :rest}
309
+ scope :mix_args, {:id => :req, :vararg => :rest}
310
+ end
311
+ end
312
+
313
+ it "should be able to query scopes on the current model" do
314
+ ScopeResource.no_arg.to_query.should eql "no_arg=true"
315
+ # ScopeResource.one_arg(5).to_query.should eql "one_arg[id]=5"
316
+ # ScopeResource.one_array_arg([3, 5]).to_query.should eql "one_array_arg[ids][]=3&one_array_arg[ids][]=5"
317
+ # ScopeResource.two_args(1, 20).to_query.should eql "two_args[page]=1&two_args[per_page]=20"
318
+ # ScopeResource.opt_arg.to_query.should eql "opt_args=true"
319
+ # ScopeResource.opt_args(3).to_query.should eql "opt_args[arg1]=3"
320
+ # ScopeResource.var_args(1, 2).to_query.should eql "var_args[ids][]=1&var_args[ids][]=2"
321
+ # ScopeResource.mix_args("a", {:opt1 => 1}, {:opt2 => 2}).to_query.should eql "mix_args[arg1]=a&mix_args[vararg][][opt1]=1&mix_arg[vararg][][opt2]=2"
322
+ end
323
+
324
+ it "should be able to change scopes" do
325
+ ap = Associations::MultiObjectProxy.new("TestResource", [{:service_uri => "/route", :scope1 => {"scope1" => true}, :scope2 => {"scope2" => true}}])
326
+ ap.scope1.should be_a(Associations::RelationScope)
327
+ ap.scope1.current_scope.scope1_scope?.should be_true
328
+ end
329
+
330
+ it "should be able to chain scope calls together" do
331
+ ap = Associations::MultiObjectProxy.new("TestResource", [{:service_uri => "/route", :scope1 => {"scope1" => true}, :scope2 => {"scope2" => true}}])
332
+ ap.scope1.scope2.current_scope.scope1_and_scope2_scope?.should be_true
333
+ ap.scope1.scope2.to_query.should eql("scope1=true&scope2=true")
334
+ end
335
+
336
+ it "should support scopes that contain underscores" do
337
+ ap = Associations::MultiObjectProxy.new("TestResource", [{:service_uri => "/route", :scope_1 => {"scope_1" => true}, :scope_2 => {"scope_2" => true}}])
338
+ ap.scope_1.scope_2.current_scope.scope_1_and_scope_2_scope?.should be_true
339
+ end
340
+
341
+ it "should be able to return the current query string" do
342
+ ap = Associations::MultiObjectProxy.new("TestResource", [{:service_uri => "/route", :scope_1 => {"scope_1" => true}, :scope_2 => {"scope_2" => true}}])
343
+ ap.scope_1.scope_2.to_query.should eql("scope_1=true&scope_2=true")
344
+ end
345
+
346
+ it "should be able to substitute values into the scope query strings by passing a hash to the methods" do
347
+ ap = Associations::MultiObjectProxy.new("TestResource", [{:service_uri => "/route", :scope_1 => {"scope_1" => true, :test_sub => false}, :scope_2 => {"scope_2" => true}}])
348
+ obj = ap.scope_1(:test_sub => true).scope_2
349
+ obj.to_query.should eql("scope_1=true&scope_2=true&test_sub=true")
350
+ end
351
+ end
352
+
353
+
354
+ end
355
+
356
+ describe "Loading and Caching loaded data" do
357
+
358
+ context "Single Object" do
359
+
360
+ before(:all) do
361
+ TestResource.reload_class_attributes
362
+ end
363
+
364
+ it "should be able to force load an object" do
365
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => '/single_object_association', :active => {:active => true}, :scopes_only => true})
366
+ ap.loaded.should be_blank
367
+ name = ap.name
368
+ name.should_not be_blank
369
+ ap.loaded.should_not be_blank
370
+ # Make sure it isn't reloaded
371
+ ap.name.should eql(name)
372
+ end
373
+
374
+ it "should be able to load a scope" do
375
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => '/single_object_association', :active => {:active => true}, :scopes_only => true})
376
+ ap.internal_object.active.should be_false
377
+ ap.times_loaded.should eql(1)
378
+ ap.active.should be_a Associations::RelationScope
379
+ ap.active.name.should_not be_blank
380
+ ap.times_loaded.should eql(2)
381
+ ap.active.internal_object.active.should be_true
382
+ # another check that the resource wasn't reloaded
383
+ ap.times_loaded.should eql(2)
384
+ end
385
+
386
+ it "should be able to load a chain of scopes" do
387
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => '/single_object_association', :active => {:active => true}, :with_birthday => {:birthday => true}, :scopes_only => true})
388
+ first = ap.active.with_birthday.id
389
+ ap.with_birthday.active.id.should eql(first)
390
+ ap.times_loaded.should eql(1)
391
+ ap.active.with_birthday.birthday.should_not be_blank
392
+ end
393
+
394
+ it "should proxy unknown methods to the object loading if it hasn't already" do
395
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => '/single_object_association', :active => {:active => false}, :with_birthday => {:birthday => true}, :scopes_only => true})
396
+ ap.times_loaded.should eql(0)
397
+ ap.id.should_not be_blank
398
+ ap.times_loaded.should eql(1)
399
+ end
400
+
401
+ it "should load scopes with caching" do
402
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => '/single_object_association', :active => {:active => true}, :scopes_only => true})
403
+ ap.times_loaded.should eql(0)
404
+ ap.active.expires_in(30).internal_object
405
+ ap.active.expires_in(30).internal_object
406
+ ap.times_loaded.should eql(1)
407
+ end
408
+
409
+ it "should check that ttl matches the expiration parameter" do
410
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => '/single_object_association', :active => {:active => true}, :scopes_only => true})
411
+ ap.active.expires_in(10).ttl.should eql(10)
412
+ end
413
+
414
+ it "should cache scopes when caching enabled" do
415
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => '/single_object_association', :active => {:active => false}, :with_birthday => {:birthday => true}, :scopes_only => true})
416
+ ApiResource.expects(:with_ttl).with(10)
417
+ ap.active(:active => true, :expires_in => 10).internal_object
418
+ end
419
+
420
+ it "should only load each distinct set of scopes once" do
421
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => '/single_object_association', :active => {:active => false}, :with_birthday => {:birthday => true}, :scopes_only => true})
422
+ ap.times_loaded.should eql(0)
423
+ ap.active.with_birthday.internal_object
424
+ ap.active.with_birthday.internal_object
425
+ ap.with_birthday.active.internal_object
426
+ ap.times_loaded.should eql(1)
427
+ end
428
+
429
+ it "should be able to clear it's loading cache" do
430
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => '/single_object_association', :active => {:active => true}, :with_birthday => {:birthday => true}, :scopes_only => true})
431
+ ap.active.internal_object
432
+ ap.times_loaded.should eql(1)
433
+ ap.reload
434
+ ap.active.internal_object
435
+ ap.times_loaded.should eql(1)
436
+ end
437
+
438
+ end
439
+
440
+ it "should be able to reload a single-object association" do
441
+ ap = Associations::SingleObjectProxy.new("TestResource",{:service_uri => '/single_object_association', :active => {:active => true}, :scopes_only => true})
442
+
443
+ old_name = ap.name
444
+
445
+ str = "krdflkjsd"
446
+
447
+ ap.name = str
448
+ ap.name.should eql str
449
+ ap.reload
450
+
451
+ ap.name.should eql old_name
452
+ end
453
+
454
+ it "should be able to reload a multi-object association" do
455
+ ap = Associations::MultiObjectProxy.new("TestResource",{:service_uri => '/multi_object_association', :active => {:active => false}, :inactive => {:active => false}, :with_birthday => {:birthday => true}})
456
+
457
+ old_name = ap.first.name
458
+
459
+ str = "krdflkjsd"
460
+
461
+ ap.first.name = str
462
+ ap.first.name.should eql str
463
+
464
+ ap.reload
465
+
466
+ ap.first.name.should eql old_name
467
+ end
468
+
469
+ context "Multi Object" do
470
+
471
+ it "should be able to load 'all'" do
472
+ ap = Associations::MultiObjectProxy.new("TestResource",{:service_uri => '/multi_object_association', :active => {:active => false}, :inactive => {:active => false}, :with_birthday => {:birthday => true}})
473
+ results = ap.all
474
+ results.size.should eql(5)
475
+ results.first.active.should be_false
476
+ end
477
+
478
+ it "should be able to load a scope" do
479
+ ap = Associations::MultiObjectProxy.new("TestResource",{:service_uri => '/multi_object_association', :active => {:active => false}, :inactive => {:active => false}, :with_birthday => {:birthday => true}})
480
+ results = ap.active.internal_object
481
+ results.size.should eql(5)
482
+ results.first.active.should be_true
483
+ end
484
+
485
+ it "should be able to load a chain of scopes" do
486
+ ap = Associations::MultiObjectProxy.new("TestResource",{:service_uri => '/multi_object_association', :active => {:active => true}, :inactive => {:active => false}, :with_birthday => {:birthday => true}})
487
+ results = ap.active.with_birthday.internal_object
488
+ results.first.active.should be_true
489
+ results.first.birthday.should_not be_blank
490
+ end
491
+
492
+ it "should be able to load a chain of scopes with substitution" do
493
+ ap = Associations::MultiObjectProxy.new("TestResource",{:service_uri => '/multi_object_association', :active => {:active => true}, :inactive => {:active => false}, :with_birthday => {:birthday => true}})
494
+ results = ap.inactive(:active => true).with_birthday.internal_object
495
+ results.first.active.should be_true
496
+ results.first.birthday.should_not be_blank
497
+ end
498
+
499
+ it "should proxy unknown methods to the object array loading if it hasn't already" do
500
+ ap = Associations::MultiObjectProxy.new("TestResource",{:service_uri => '/multi_object_association', :active => {:active => true}, :inactive => {:active => false}, :with_birthday => {:birthday => true}})
501
+ ap.first.should be_a TestResource
502
+ ap.active.first.should be_a TestResource
503
+ ap.times_loaded.should eql(2)
504
+ end
505
+
506
+ it "should only load each distinct set of scopes once" do
507
+ ap = Associations::MultiObjectProxy.new("TestResource",{:service_uri => '/multi_object_association', :active => {:active => true}, :inactive => {:active => false}, :with_birthday => {:birthday => true}})
508
+ ap.first
509
+ ap.active.first
510
+ ap.times_loaded.should eql(2)
511
+ ap.active.first
512
+ ap.times_loaded.should eql(2)
513
+ ap.active.with_birthday.first
514
+ ap.with_birthday.active.first
515
+ ap.times_loaded.should eql(3)
516
+ end
517
+
518
+ it "should be able to clear it's loading cache" do
519
+ ap = Associations::MultiObjectProxy.new("TestResource",{:service_uri => '/multi_object_association', :active => {:active => true}, :inactive => {:active => false}, :with_birthday => {:birthday => true}})
520
+ ap.active.first
521
+ ap.times_loaded.should eql(1)
522
+ ap.reload
523
+ ap.active.first
524
+ ap.times_loaded.should eql(1)
525
+ end
526
+
527
+ it "should be enumerable" do
528
+ ap = Associations::MultiObjectProxy.new("TestResource",{:service_uri => '/multi_object_association', :active => {:active => true}, :inactive => {:active => false}, :with_birthday => {:birthday => true}})
529
+ ap.each do |tr|
530
+ tr.name.should_not be_blank
531
+ end
532
+ end
533
+
534
+ end
535
+
536
+ context "Scopes" do
537
+
538
+ before(:all) do
539
+ TestResource.reload_class_attributes
540
+ end
541
+
542
+ it "should define class methods for the known scopes" do
543
+ TestResource.scopes.each do |key, _|
544
+ TestResource.should respond_to key
545
+ end
546
+ end
547
+
548
+ it "should return a ResourceScope when calling any scope on a class" do
549
+ TestResource.send(TestResource.scopes.first.first.to_sym).should be_a Associations::ResourceScope
550
+ end
551
+
552
+ it "should be able to chain scopes" do
553
+ scp = TestResource.active.paginate
554
+ scp.should be_a Associations::ResourceScope
555
+ scp.to_query.should eql("active=true&paginate=true")
556
+ end
557
+
558
+ it "should load when calling all" do
559
+ TestResource.active.should respond_to :all
560
+ TestResource.active.should respond_to :internal_object
561
+ results = TestResource.active.all
562
+ results.should be_a Array
563
+ results.size.should eql(5)
564
+ results.each do |res|
565
+ res.should be_a TestResource
566
+ end
567
+ end
568
+
569
+ it "should load when calling an enumerable method or an array method" do
570
+ TestResource.active.each do |result|
571
+ result.should be_a TestResource
572
+ end
573
+ end
574
+ end
575
+
576
+ context "Assigning Data" do
577
+ context "Single Object Association" do
578
+ before(:all) do
579
+ TestResource.has_one(:has_one_object)
580
+ TestResource.belongs_to(:belongs_to_object)
581
+ end
582
+ after(:all) do
583
+ TestResource.reload_class_attributes
584
+ end
585
+
586
+ it "should assign associations to the correct type on initialization" do
587
+ #binding.pry
588
+ tr = TestResource.new(:has_one_object => {:name => "Dan"}, :belongs_to_object => {:name => "Dan"})
589
+
590
+ tr.has_one_object.internal_object.should be_instance_of HasOneObject
591
+ tr.belongs_to_object.internal_object.should be_instance_of BelongsToObject
592
+
593
+ end
594
+
595
+ it "should assign associations to the correct type when setting attributes directly" do
596
+ tr = TestResource.new()
597
+ tr.has_one_object = {:name => "Dan"}
598
+ tr.belongs_to_object = {:name => "Dan"}
599
+
600
+ tr.has_one_object.internal_object.should be_instance_of HasOneObject
601
+ tr.belongs_to_object.internal_object.should be_instance_of BelongsToObject
602
+ end
603
+
604
+ it "should be able to reload a single-object association" do
605
+ ApiResource::Associations::SingleObjectProxy.any_instance.stubs(:remote_path => "/has_one_objects")
606
+ HasOneObject.connection.stubs(:get => nil)
607
+
608
+ tr = TestResource.new()
609
+
610
+ tr.has_one_object = {:color => "Blue"}
611
+
612
+ tr.has_one_object.reload
613
+ tr.has_one_object.instance_variable_get(:@internal_object).should be_blank
614
+ end
615
+
616
+ end
617
+
618
+ context "Multi Object Association" do
619
+ before(:all) do
620
+ TestResource.has_many(:has_many_objects)
621
+ end
622
+ after(:all) do
623
+ TestResource.reload_class_attributes
624
+ end
625
+
626
+ it "should assign associations to the correct type on initialization" do
627
+ tr = TestResource.new(:has_many_objects => [{:name => "Dan"}])
628
+ tr.has_many_objects.internal_object.first.should be_instance_of HasManyObject
629
+
630
+ end
631
+
632
+ it "should assign associations to the correct type when setting attributes directly" do
633
+ tr = TestResource.new()
634
+ tr.has_many_objects = [{:name => "Dan"}]
635
+ tr.has_many_objects.internal_object.first.should be_instance_of HasManyObject
636
+ end
637
+
638
+ it "should be able to reload a multi-object association" do
639
+ ApiResource::Associations::MultiObjectProxy.any_instance.stubs(:remote_path => "/has_many_objects")
640
+ ApiResource::Connection.any_instance.stubs(:get => [])
641
+
642
+ tr = TestResource.new(:has_many_objects => [{:color => "blue"}])
643
+
644
+ tr.has_many_objects.reload
645
+
646
+ tr.has_many_objects.should be_blank
647
+ end
648
+
649
+ end
650
+
651
+ context "ActiveModel" do
652
+ before(:all) do
653
+ require 'active_record'
654
+ db_path = File.expand_path(File.dirname(__FILE__) + "/../tmp/api_resource_test_db.sqlite")
655
+ ActiveRecord::Base.establish_connection({"adapter" => "sqlite3", "database" => db_path})
656
+ ActiveRecord::Base.connection.create_table(:test_ars, :force => true) do |t|
657
+ t.integer(:test_resource_id)
658
+ end
659
+ ApiResource::Associations.activate_active_record
660
+ TestAR = Class.new(ActiveRecord::Base)
661
+ TestAR.class_eval do
662
+ belongs_to_remote :my_favorite_thing, :class_name => "TestClassYay"
663
+ end
664
+ end
665
+ it "should define remote association types for AR" do
666
+ [:has_many_remote, :belongs_to_remote, :has_one_remote].each do |assoc|
667
+ ActiveRecord::Base.singleton_methods.should include assoc
668
+ end
669
+ end
670
+ it "should add remote associations to related objects" do
671
+ TestAR.related_objects.should eql({"has_many_remote"=>{}, "belongs_to_remote"=>{"my_favorite_thing"=>"TestClassYay"}, "has_one_remote"=>{}, "scopes"=>{}})
672
+ end
673
+ context "Not Overriding Scopes" do
674
+ it "should not override scopes, which would raise an error with lambda-style scopes" do
675
+ lambda {
676
+ TestAR.class_eval do
677
+ scope :my_favorite_scope, lambda {
678
+ joins(:my_test)
679
+ }
680
+ end
681
+ }.should_not raise_error
682
+ end
683
+ end
684
+ context "Belongs To" do
685
+ before(:all) do
686
+ TestAR.class_eval do
687
+ belongs_to_remote :test_resource
688
+ end
689
+ end
690
+ it "should attempt to load a single remote object for a belongs_to relationship" do
691
+ tar = TestAR.new
692
+ tar.stubs(:test_resource_id).returns(1)
693
+ TestResource.connection.expects(:get).with("/test_resources/1.json").once.returns({"name" => "testing"})
694
+ # load the test resource
695
+ tar.test_resource.name.should eql "testing"
696
+ end
697
+ end
698
+ context "Has One" do
699
+ before(:all) do
700
+ TestAR.class_eval do
701
+ has_one_remote :test_resource
702
+ end
703
+ end
704
+ it "should attempt to load a single remote object for a has_one relationship" do
705
+ tar = TestAR.new
706
+ tar.stubs(:id).returns(1)
707
+ TestResource.connection.expects(:get).with("/test_resources.json?test_ar_id=1").once.returns([{"name" => "testing"}])
708
+ # load the test resource
709
+ tar.test_resource.name.should eql "testing"
710
+ end
711
+ end
712
+ context "Has Many" do
713
+ before(:all) do
714
+ TestAR.class_eval do
715
+ has_many_remote :has_many_objects
716
+ end
717
+ end
718
+ it "should attempt to load a collection of remote objects for a has_many relationship" do
719
+ tar = TestAR.new
720
+ tar.stubs(:id).returns(1)
721
+ HasManyObject.connection.expects(:get).with("/has_many_objects.json?test_ar_id=1").once.returns([{"name" => "testing"}])
722
+ # load the test resource
723
+ tar.has_many_objects.first.name.should eql "testing"
724
+ end
725
+ end
726
+ context "Has Many Through" do
727
+ before(:all) do
728
+ TestAR.class_eval do
729
+ self.extend ApiResource::Associations::HasManyThroughRemoteObjectProxy
730
+ has_many :test_throughs
731
+ has_many_through_remote(:belongs_to_objects, :through => :test_throughs)
732
+ end
733
+ end
734
+ it "should attempt to load a collection of remote objects for a has_many_through relationship" do
735
+ tar = TestAR.new
736
+ through_test_resource_1 = TestThrough.new
737
+ through_test_resource_2 = TestThrough.new
738
+ belongs_to_object_1 = BelongsToObject.new
739
+ belongs_to_object_2 = BelongsToObject.new
740
+ tar.expects(:test_throughs).returns([through_test_resource_1, through_test_resource_2])
741
+ through_test_resource_1.expects(:belongs_to_object).returns([belongs_to_object_1])
742
+ through_test_resource_2.expects(:belongs_to_object).returns([belongs_to_object_2])
743
+
744
+ tar.belongs_to_objects.should eql [belongs_to_object_1, belongs_to_object_2]
745
+ end
746
+
747
+ end
748
+ end
749
+ end
750
+ end
751
+ end