api_resource 0.6.21 → 0.6.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/Gemfile.lock +1 -1
  4. data/LICENSE.txt +22 -0
  5. data/README.md +16 -9
  6. data/docs/Attributes.md +64 -0
  7. data/docs/Caching.md +45 -0
  8. data/docs/GettingStarted.md +149 -0
  9. data/docs/Relationships.md +136 -0
  10. data/docs/ResourceDefinition.md +80 -0
  11. data/docs/Retrieval.md +279 -0
  12. data/docs/Serialization.md +56 -0
  13. data/lib/api_resource/associations/has_many_remote_object_proxy.rb +2 -2
  14. data/lib/api_resource/attributes.rb +16 -4
  15. data/lib/api_resource/base.rb +98 -19
  16. data/lib/api_resource/conditions/abstract_condition.rb +241 -129
  17. data/lib/api_resource/conditions/include_condition.rb +7 -1
  18. data/lib/api_resource/conditions/pagination_condition.rb +37 -0
  19. data/lib/api_resource/conditions/where_condition.rb +19 -0
  20. data/lib/api_resource/conditions.rb +18 -2
  21. data/lib/api_resource/connection.rb +27 -13
  22. data/lib/api_resource/exceptions.rb +11 -11
  23. data/lib/api_resource/finders/abstract_finder.rb +176 -95
  24. data/lib/api_resource/finders/multi_object_association_finder.rb +10 -9
  25. data/lib/api_resource/finders/resource_finder.rb +59 -49
  26. data/lib/api_resource/finders/single_finder.rb +5 -6
  27. data/lib/api_resource/finders/single_object_association_finder.rb +52 -51
  28. data/lib/api_resource/finders.rb +1 -1
  29. data/lib/api_resource/formats/file_upload_format.rb +75 -0
  30. data/lib/api_resource/formats.rb +4 -1
  31. data/lib/api_resource/response.rb +108 -0
  32. data/lib/api_resource/scopes.rb +62 -5
  33. data/lib/api_resource/serializer.rb +1 -1
  34. data/lib/api_resource/typecasters/boolean_typecaster.rb +1 -0
  35. data/lib/api_resource/typecasters/integer_typecaster.rb +1 -0
  36. data/lib/api_resource/typecasters/time_typecaster.rb +12 -4
  37. data/lib/api_resource/version.rb +1 -1
  38. data/lib/api_resource.rb +1 -0
  39. data/spec/lib/associations/has_one_remote_object_proxy_spec.rb +4 -4
  40. data/spec/lib/associations_spec.rb +3 -3
  41. data/spec/lib/attributes_spec.rb +16 -1
  42. data/spec/lib/base_spec.rb +121 -39
  43. data/spec/lib/conditions/{abstract_conditions_spec.rb → abstract_condition_spec.rb} +23 -11
  44. data/spec/lib/conditions/pagination_condition_spec.rb +88 -0
  45. data/spec/lib/finders/multi_object_association_finder_spec.rb +55 -27
  46. data/spec/lib/finders/resource_finder_spec.rb +26 -2
  47. data/spec/lib/finders/single_object_association_finder_spec.rb +14 -6
  48. data/spec/lib/finders_spec.rb +81 -81
  49. data/spec/lib/observing_spec.rb +3 -4
  50. data/spec/lib/response_spec.rb +18 -0
  51. data/spec/lib/scopes_spec.rb +25 -1
  52. data/spec/lib/typecasters/boolean_typecaster_spec.rb +1 -1
  53. data/spec/lib/typecasters/integer_typecaster_spec.rb +1 -1
  54. data/spec/lib/typecasters/time_typecaster_spec.rb +6 -0
  55. data/spec/support/files/bg-awesome.jpg +0 -0
  56. data/spec/support/mocks/test_resource_mocks.rb +26 -16
  57. data/spec/support/requests/test_resource_requests.rb +27 -23
  58. metadata +24 -4
@@ -0,0 +1,108 @@
1
+ module ApiResource
2
+ class Response
3
+
4
+ attr_accessor :body
5
+ attr_reader :headers
6
+
7
+ # explicit delegate for things we define
8
+ delegate(
9
+ :as_json,
10
+ :blank?,
11
+ :inspect,
12
+ :to_s,
13
+ to: :body
14
+ )
15
+
16
+ #
17
+ # Constructor
18
+ #
19
+ # @param response [HttpClient::Response]
20
+ # @param format [ApiResource::Format] Decoder
21
+ #
22
+ def initialize(response)
23
+ @body = self.parse_body(response)
24
+ @headers = response.try(:headers) || {}
25
+ end
26
+
27
+ #
28
+ # We typically treat the response as just a wrapper for an
29
+ # Array or a Hash and use is_a? to determine what kind of response
30
+ # we have. So to keep that consistent, #is_a? returns true if the
31
+ # body is a descendant of the class as well
32
+ #
33
+ # @param klass [Class]
34
+ #
35
+ # @return [Boolean]
36
+ def is_a?(klass)
37
+ super || @body.is_a?(klass)
38
+ end
39
+
40
+ #
41
+ # Implementation of marshal_dump
42
+ #
43
+ # @return [Array<Hash>] The data to dump
44
+ def marshal_dump
45
+ [@body, @headers]
46
+ end
47
+
48
+ #
49
+ # Implementation of marshal load
50
+ # @param args [Array] Array of dumped data
51
+ #
52
+ # @return [Response] New instance
53
+ def marshal_load(args)
54
+ @body, @headers = args
55
+ self
56
+ end
57
+
58
+
59
+ #
60
+ # Return ourself cloned with the body wrapped as an array
61
+ #
62
+ # @example
63
+ # resp = ApiResource::Response.new(response, format)
64
+ # resp.body # => {'a' => 'b'}
65
+ #
66
+ # array = Array.wrap(resp)
67
+ # array.body # => [{'a' => 'b'}]
68
+ #
69
+ # array.response == resp.response # true!
70
+ #
71
+ # @return [ApiResource::Response]
72
+ def to_ary
73
+ klone = self.dup
74
+ klone.body = Array.wrap(self.body)
75
+ klone
76
+ end
77
+
78
+ protected
79
+
80
+ #
81
+ # Proxy method missing to the body
82
+ #
83
+ # @param meth [Symbol] Method called
84
+ # @param *args [Array<Mixed>] Args passed
85
+ # @param &block [Proc] Block passed
86
+ #
87
+ # @return [Mixed]
88
+ def method_missing(meth, *args, &block)
89
+ @body.__send__(meth, *args, &block)
90
+ end
91
+
92
+ #
93
+ # Handle parsing of the body. Returns a blank Hash if
94
+ # no body is present
95
+ #
96
+ # @param response [HttpClient::Response]
97
+ #
98
+ # @return [Hash, Array<Hash>] Parsed response
99
+ def parse_body(response)
100
+ if response.try(:body).present?
101
+ return ApiResource.format.decode(response.body)
102
+ else
103
+ return {}
104
+ end
105
+ end
106
+
107
+ end
108
+ end
@@ -6,6 +6,7 @@ module ApiResource
6
6
  module ClassMethods
7
7
  # TODO: calling these methods should force loading of the resource definition
8
8
  def scopes
9
+ self.reload_resource_definition
9
10
  return self.related_objects[:scopes]
10
11
  end
11
12
 
@@ -68,7 +69,7 @@ module ApiResource
68
69
  end
69
70
  end
70
71
 
71
- #
72
+ #
72
73
  # Apply scopes from params based on our resource
73
74
  # definition
74
75
  #
@@ -90,20 +91,53 @@ module ApiResource
90
91
  return base
91
92
  end
92
93
 
94
+ #
95
+ # Add our dynamic scopes based on a set of params
96
+ #
97
+ # @param params [Hash] User-supplied params
98
+ # @param base [ApiResource::Conditions::AbstractCondition, Class] Base
99
+ # Scope
100
+ #
101
+ # @return [ApiResource::Conditions::AbstractCondition] [description]
93
102
  def add_dynamic_scopes(params, base)
94
103
  self.dynamic_scopes.each_pair do |name, args|
95
- next if params[name].blank?
104
+ # make sure we have all required arguments
105
+ next unless self.check_required_scope_args(args, params[name])
106
+
107
+ # the args we will apply
96
108
  caller_args = []
97
- args.each_pair do |subkey, type|
98
- if type == :req || params[name][subkey].present?
99
- caller_args << params[name][subkey]
109
+
110
+ # iterate through our args and add them to an array to send to our
111
+ # scope
112
+ args.keys.each do |subkey|
113
+ # we only apply things that are present or explicitly false
114
+ if val = self.get_scope_arg_value(subkey, params[name][subkey])
115
+ caller_args << val
100
116
  end
101
117
  end
118
+ # call our scope with the supplied args
102
119
  base = base.send(name, *caller_args)
103
120
  end
104
121
  return base
105
122
  end
106
123
 
124
+ #
125
+ # Check if we have supplied all of the necessary
126
+ # @param scope [Hash] [Scope Definition
127
+ # @param params [Hash] [Supplied params]
128
+ #
129
+ # @return [Boolean] Validity
130
+ def check_required_scope_args(scope, params)
131
+ # make sure we have a hash and it has values
132
+ return false unless params.is_a?(Hash) && params.present?
133
+ # find required values
134
+ required = scope.select{ |k,v| v.to_sym == :req }.keys
135
+ # make sure we have all of the required values, we allow false
136
+ required.all? { |key|
137
+ params[key].present? || params[key] == false
138
+ }
139
+ end
140
+
107
141
  #
108
142
  # Wrapper method to define all scopes from the resource definition
109
143
  #
@@ -117,10 +151,33 @@ module ApiResource
117
151
  true
118
152
  end
119
153
 
154
+ #
155
+ # Scopes that require arguments
156
+ #
157
+ # @return [Hash]
120
158
  def dynamic_scopes
121
159
  self.scopes.select { |name, args| args.present? }
122
160
  end
123
161
 
162
+ #
163
+ # Get the parsed/formatted arguments to send to the server
164
+ #
165
+ # @param key [String, Symbol] Key name for the scope value
166
+ # @param value [String, Integer, Symbol] Value for the scope
167
+ #
168
+ # @return [String, Integer, Symbol, Date] Parsed/formatted value
169
+ def get_scope_arg_value(key, value)
170
+ # cast to string to avoid incorred blank? behavior for us
171
+ return "false" if value == false
172
+ # if we havea date field, try to parse, falling back to the original
173
+ # value
174
+ if key.to_s =~ /date/
175
+ value = Date.parse(value) rescue value
176
+ end
177
+ # return the final value
178
+ value
179
+ end
180
+
124
181
  def static_scopes
125
182
  self.scopes.select { |name, args| args.blank? }.keys
126
183
  end
@@ -151,7 +151,7 @@ module ApiResource
151
151
  # or if the attribute has changed to nil
152
152
  return true if self.record.changes[key].present?
153
153
  # make sure our value isn't blank
154
- return val.present?
154
+ return !val.nil?
155
155
  end
156
156
 
157
157
  #
@@ -8,6 +8,7 @@ module ApiResource
8
8
  module BooleanTypecaster
9
9
 
10
10
  def self.from_api(value)
11
+ return nil if value.nil?
11
12
  TRUE_VALUES.include?(value)
12
13
  end
13
14
 
@@ -7,6 +7,7 @@ module ApiResource
7
7
  def self.from_api(value)
8
8
  return 0 if value == false
9
9
  return 1 if value == true
10
+ return nil if value.nil?
10
11
  return nil if value.is_a?(String) && value.blank?
11
12
  return value.to_i if value.respond_to?(:to_i)
12
13
  return value.to_s.to_i
@@ -15,13 +15,17 @@ module ApiResource
15
15
 
16
16
  if value =~ ApiResource::Typecast::ISO_DATETIME
17
17
  micro = ($7.to_f * 1_000_000).to_i
18
- return self.new_time($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, micro)
18
+ return self.new_time(false, $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, micro)
19
19
  end
20
20
 
21
21
  time_info = Date._parse(value)
22
22
  time_info[:micro] = ((time_info[:sec_fraction].to_f % 1) * 1_000_000).to_i
23
23
 
24
- new_time(*time_info.values_at(:year, :mon, :mday, :hour, :min, :sec, :micro))
24
+ if time_info[:zone].present?
25
+ new_time(true, *time_info.values_at(:year, :mon, :mday, :hour, :min, :sec, :zone))
26
+ else
27
+ new_time(false, *time_info.values_at(:year, :mon, :mday, :hour, :min, :sec, :micro))
28
+ end
25
29
 
26
30
  end
27
31
 
@@ -31,10 +35,14 @@ module ApiResource
31
35
 
32
36
  protected
33
37
 
34
- def self.new_time(*args)
38
+ def self.new_time(use_zone, *args)
35
39
  year = args.first
36
40
  return nil if year.nil? || year == 0
37
- Time.utc(*args) rescue nil
41
+ if use_zone
42
+ Time.new(*args).utc rescue nil
43
+ else
44
+ Time.utc(*args) rescue nil
45
+ end
38
46
  end
39
47
 
40
48
  end
@@ -1,3 +1,3 @@
1
1
  module ApiResource
2
- VERSION = "0.6.21"
2
+ VERSION = "0.6.22"
3
3
  end
data/lib/api_resource.rb CHANGED
@@ -33,6 +33,7 @@ module ApiResource
33
33
  autoload :Mocks
34
34
  autoload :ModelErrors
35
35
  autoload :Observing
36
+ autoload :Response
36
37
  autoload :Scopes
37
38
  autoload :Serializer
38
39
  autoload :Typecast
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  module ApiResource
4
4
  module Associations
5
-
5
+
6
6
  describe HasOneRemoteObjectProxy do
7
7
 
8
8
  before(:all) do
@@ -10,12 +10,12 @@ module ApiResource
10
10
  end
11
11
 
12
12
  context "#load" do
13
-
13
+
14
14
  it "correctly loads data from an endpoint that returns
15
15
  a single record" do
16
-
16
+
17
17
  tr = TestResource.new(
18
- :has_one_object => {:service_uri => "/has_one_objects/1.json"}
18
+ :has_one_object => {:service_uri => "/has_one_objects/1.json"}
19
19
  )
20
20
  tr.has_one_object.internal_object.should be_instance_of(
21
21
  HasOneObject
@@ -604,10 +604,10 @@ describe "Associations" do
604
604
  end
605
605
 
606
606
  it "should be able to chain scopes" do
607
- scp = TestResource.active.paginate(20, 1)
607
+ scp = TestResource.active.boolean(20, 1)
608
608
  scp.should be_a Conditions::ScopeCondition
609
609
  scp.to_query.should eql(
610
- "active=true&paginate[current_page]=1&paginate[per_page]=20"
610
+ "active=true&boolean[a]=20&boolean[b]=1"
611
611
  )
612
612
  end
613
613
 
@@ -789,7 +789,7 @@ describe "Associations" do
789
789
  end
790
790
  end
791
791
  context "Belongs To" do
792
- before(:all) do
792
+ before(:all) do
793
793
  TestAR.class_eval do
794
794
  belongs_to_remote :test_resource
795
795
  end
@@ -70,7 +70,7 @@ describe "Attributes" do
70
70
 
71
71
  it "should be able to determine scopes when the class loads" do
72
72
  tst = TestResource.new
73
- tst.scope?(:paginate).should be_true
73
+ tst.scope?(:boolean).should be_true
74
74
  tst.scope?(:active).should be_true
75
75
  end
76
76
 
@@ -104,6 +104,21 @@ describe "Attributes" do
104
104
 
105
105
  tst.attr5.should eql("attr5")
106
106
  end
107
+
108
+ context '#password' do
109
+
110
+ it 'is able to define password even though it is a
111
+ class attribute' do
112
+
113
+ TestResource.define_attributes :password
114
+
115
+ resource = TestResource.new(password: '123456')
116
+ expect(resource.password).to eql('123456')
117
+
118
+ end
119
+
120
+ end
121
+
107
122
  end
108
123
 
109
124
  it "should define methods for testing for reading and writing known attributes" do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require 'spec_helper'
2
2
  require 'json'
3
3
 
4
4
  include ApiResource
@@ -93,6 +93,8 @@ describe "Base" do
93
93
  self.prefix = "/path/to/nested/:id/"
94
94
  end
95
95
 
96
+ ChildResource = Class.new(PrefixResource)
97
+
96
98
  end
97
99
 
98
100
 
@@ -108,6 +110,43 @@ describe "Base" do
108
110
  )
109
111
  end
110
112
 
113
+ it "appends its type in the query string if it is not a direct
114
+ descendant of ApiResource::Base" do
115
+
116
+ expect(ChildResource.new_element_path).to eql(
117
+ "/path/to/project/prefix_resources/new.json?type=ChildResource"
118
+ )
119
+
120
+ end
121
+
122
+ end
123
+
124
+ context "#id" do
125
+
126
+ it 'should have a blank id on initialize' do
127
+ expect(TestResource.new.id).to be_nil
128
+ end
129
+
130
+ end
131
+
132
+ context '#new_record?' do
133
+
134
+ it 'is a new record on initialize' do
135
+ expect(TestResource.new).to be_new_record
136
+ end
137
+
138
+ end
139
+
140
+ context '#to_key' do
141
+
142
+ it 'is the primary key of the record' do
143
+ expect(TestResource.find(3).to_key).to eq([3])
144
+ end
145
+
146
+ it 'is nil if the record is not persisted' do
147
+ expect(TestResource.new.to_key).to be_nil
148
+ end
149
+
111
150
  end
112
151
 
113
152
  context "Prefixes" do
@@ -127,11 +166,9 @@ describe "Base" do
127
166
 
128
167
  TestResource.connection.expects(:post).with(
129
168
  "/belongs_to_objects/22/test_resources.json",
130
- {
131
- test_resource: {
132
- 'name' => 'Dan'
133
- }
134
- },
169
+ has_entries({
170
+ test_resource: is_a(Hash)
171
+ }),
135
172
  TestResource.headers
136
173
  )
137
174
 
@@ -172,6 +209,53 @@ describe "Base" do
172
209
 
173
210
  end
174
211
 
212
+ context "Saving with file upload" do
213
+
214
+ before(:all) do
215
+ TestResource.reload_resource_definition
216
+ end
217
+
218
+ after(:all) do
219
+ TestResource.reload_resource_definition
220
+ end
221
+
222
+ let(:uploaded_file){
223
+ ActionDispatch::Http::UploadedFile.new(
224
+ tempfile: File.new(
225
+ File.expand_path("../../support/files/bg-awesome.jpg", __FILE__)
226
+ )
227
+ )
228
+ }
229
+
230
+ context "#save" do
231
+
232
+ it 'sends the file to HTTPClient' do
233
+
234
+ TestResource.define_attributes :image_file
235
+
236
+ TestResource.connection.expects(:request)
237
+ .with(
238
+ :post,
239
+ instance_of(String),
240
+ has_entries(
241
+ test_resource: has_entries(
242
+ 'image_file' => instance_of(File)
243
+ )
244
+ ),
245
+ instance_of(Hash)
246
+ )
247
+
248
+ test_resource = TestResource.new(
249
+ image_file: uploaded_file
250
+ )
251
+ test_resource.save
252
+
253
+ end
254
+
255
+ end
256
+
257
+ end
258
+
175
259
 
176
260
  describe "Loading data from a hash" do
177
261
 
@@ -445,19 +529,23 @@ describe "Base" do
445
529
  context("Override create to return the json") do
446
530
 
447
531
 
448
- it "should be able to include associations when saving if they are specified" do
532
+ it "should be able to include associations when saving if
533
+ they are specified" do
449
534
  ApiResource::Connection.any_instance.expects(:post).with(
450
535
  "/test_resources.json",
451
- {
452
- test_resource: {
453
- 'name' => 'Ethan',
454
- 'age' => 20
455
- }
456
- },
536
+ has_entries({
537
+ test_resource: has_entries({
538
+ 'has_many_objects' => [{}]
539
+ })
540
+ }),
457
541
  TestResource.headers
458
542
  )
459
543
 
460
- tr = TestResource.build(name: "Ethan", age: 20)
544
+ tr = TestResource.build(
545
+ name: "Ethan",
546
+ age: 20,
547
+ has_many_objects: [HasManyObject.new]
548
+ )
461
549
  tr.save
462
550
  end
463
551
 
@@ -467,7 +555,8 @@ describe "Base" do
467
555
  "/test_resources.json",
468
556
  {
469
557
  test_resource: {
470
- 'name' => 'Ethan'
558
+ 'name' => 'Ethan',
559
+ 'roles' => []
471
560
  }
472
561
  },
473
562
  TestResource.headers
@@ -477,13 +566,15 @@ describe "Base" do
477
566
  tr.save
478
567
  end
479
568
 
480
- it "should include false attributes when creating by default" do
569
+ it "should include false and empty attributes when creating
570
+ by default" do
481
571
  ApiResource::Connection.any_instance.expects(:post).with(
482
572
  "/test_resources.json",
483
573
  {
484
574
  test_resource: {
485
575
  'name' => 'Ethan',
486
- 'is_active' => false
576
+ 'is_active' => false,
577
+ 'roles' => []
487
578
  }
488
579
  },
489
580
  TestResource.headers
@@ -500,6 +591,7 @@ describe "Base" do
500
591
  {
501
592
  test_resource: {
502
593
  'name' => 'Ethan',
594
+ 'roles' => [],
503
595
  'has_one_object' => {
504
596
  'size' => 'large'
505
597
  }
@@ -514,15 +606,15 @@ describe "Base" do
514
606
  end
515
607
 
516
608
 
517
- it "should include nil attributes if they are passed in through the include_extras" do
609
+ it "should include nil attributes if they are passed in through
610
+ the include_extras" do
518
611
  ApiResource::Connection.any_instance.expects(:post).with(
519
612
  "/test_resources.json",
520
- {
521
- test_resource: {
522
- 'name' => 'Ethan',
613
+ has_entries({
614
+ test_resource: has_entries({
523
615
  'age' => nil
524
- }
525
- },
616
+ })
617
+ }),
526
618
  TestResource.headers
527
619
  )
528
620
 
@@ -904,7 +996,7 @@ describe "Base" do
904
996
 
905
997
  begin
906
998
  initial = ApiResource::Base.ttl
907
- ApiResource::Base.ttl = 1
999
+ ApiResource::Base.ttl = 50
908
1000
  if defined?(Rails)
909
1001
  Object.send(:remove_const, :Rails)
910
1002
  end
@@ -914,23 +1006,13 @@ describe "Base" do
914
1006
  end
915
1007
 
916
1008
  end
917
-
918
- it "should implement caching using the ttl setting" do
919
- cache = mock(fetch: {id: 123, name: "Dan"})
920
- ApiResource.stubs(cache: cache)
921
- TestResource.find(123)
1009
+ # @todo This doesn't exactly test the cache, but stubbing is really
1010
+ # difficult when everything uses the cache
1011
+ it 'should work with the real cache' do
1012
+ tr = TestResource.find(1, expires_in: 200)
1013
+ expect(tr).to eql(TestResource.find(1, expires_in: 200))
922
1014
  end
923
1015
 
924
- it "should find with expires_in and cache" do
925
- ApiResource.cache.expects(:fetch)
926
- .with(anything, expires_in: 10.0)
927
- .returns({id: 2, name: "d"})
928
-
929
- res = TestResource.find("adfa", expires_in: 10)
930
-
931
- ApiResource::Base.ttl.should eql(1)
932
- res.id.to_i.should eql(2)
933
- end
934
1016
  end
935
1017
 
936
1018
  end
@@ -17,7 +17,7 @@ describe "Conditions" do
17
17
 
18
18
  obj3 = obj.paginate
19
19
 
20
- obj3.to_query.should eql("active=true&paginate=true")
20
+ obj3.to_query.should eql("active=true&page=1&per_page=10")
21
21
  obj3.should_not be_eager_load
22
22
  obj3.should_not be_blank_conditions
23
23
  # Make sure that it clones
@@ -61,25 +61,37 @@ describe "Conditions" do
61
61
  it "should create a resource finder when forced to load, and cache the result" do
62
62
  obj = TestResource.includes(:has_many_objects)
63
63
 
64
- ApiResource::Finders::ResourceFinder.expects(:new).with(TestResource, obj).returns(mock(:load => [1]))
65
- obj.internal_object.should eql([1])
66
- obj.all.should eql([1])
64
+ resource_finder_mock = mock(load: [1], first: 1)
65
+
66
+ ApiResource::Finders::ResourceFinder.expects(:new)
67
+ .with(TestResource, obj)
68
+ .returns(resource_finder_mock)
69
+
70
+ obj.internal_object.should eql(resource_finder_mock)
71
+ obj.all.should eql(resource_finder_mock)
67
72
  obj.first.should eql(1)
68
73
  end
69
74
 
70
- it "should proxy calls to enumerable and array methods to the loaded object" do
75
+ it "should proxy calls to enumerable and array methods to the loaded
76
+ object" do
77
+
71
78
  obj = TestResource.includes(:has_many_objects)
72
79
 
73
- ApiResource::Finders::ResourceFinder.expects(:new).with(TestResource, obj).returns(mock(:load => [1,2]))
80
+ # this just needs to call each
81
+ resource_finder_mock = mock(load: true, each: true)
82
+
83
+ ApiResource::Finders::ResourceFinder.expects(:new)
84
+ .with(TestResource, obj)
85
+ .returns(resource_finder_mock)
74
86
 
75
- obj.collect{|o| o * 2}.should eql([2,4])
87
+ obj.collect{|o| o * 2}.should eql([])
76
88
  end
77
89
 
78
90
  it "should not typecast nil and false to true when creating condition hashes" do
79
- obj = TestResource.paginate(false, nil)
80
- hsh = obj.to_hash["paginate"]
81
- hsh["per_page"].should eql(false)
82
- hsh.should be_key(:current_page)
91
+ obj = TestResource.boolean(false, nil)
92
+ hsh = obj.to_hash["boolean"]
93
+ hsh["a"].should eql(false)
94
+ hsh.should be_key(:b)
83
95
  end
84
96
 
85
97
  end