her 0.6.4 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NzFmYjMyNGNlODUyZjk4N2I2MzA5ZGZmYjdkNTQyYTQ5YzZiMzlhMw==
4
+ ZGIzMGM1MTY2YmRmZjZjNmJhNzhhOGQ0YjcxZmMxZDM3MTJlNWVlZg==
5
5
  data.tar.gz: !binary |-
6
- ODJkYzM0YTU3YTI3YTI2ZmNlNmIxM2UxOTAzZTc2YTA3NTJjMTM0Zg==
6
+ NTM0Y2JjYWZiNjAxOTJhMTUyZjc0OTYwZjU1YTRkNGViOWM4MTcwZA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ODEzZjJhOTYwNDM0OGI0NGQzYzBmMGVkODdiYjk3NDFkY2E5ZTNkZTRkYWZl
10
- MDlhODA5ZDY4NTJhZjE2MDM1MDIzMjViZDIzMjUxMTdhZDA1OTE4NDliODRj
11
- MDdkMzdiMTI2MzAzNDE4NjkxNDY5ZDRhNmFmNGJjMjlmODcyNjA=
9
+ MTExZDExMjk5ODU5MmQ1Nzg4YmIwNWEyYmJmZDRiMTFiMjdlNTkwNjViNzQw
10
+ YjcxN2MyM2Q2ODlmZmNkODk0OTQyOGUwY2ViMWUwMTViMWI2OTMxYWVmZjNk
11
+ NzQ0MzhiY2JlZGE2YWY4NzIyMTg1YWVhYjgwNTNhYjkzYzVjNjY=
12
12
  data.tar.gz: !binary |-
13
- MTBiODVmMmEzMDI5NTI2NmE0NGMzNDIyNWJkNjRiZTM2YTAxYzc1YjFhMTA4
14
- YTM5NWRmNzVhNjE2Yjc0MDVkYTNiMjYyY2NkMTg2ODYxYjJhZDdlZjYzNmVk
15
- NmYyNDc2M2IwYmVlNWU1YjU3Y2ZmYTE0YmQ1YWMyYjE4ZGM1YzQ=
13
+ YjJiN2Y5NGY5NWRjMTcyOTRiNDgwOGVjMjZmYTYxY2NmNjQ3NDhiNDUwMDg2
14
+ MWFhNWVkNjA3OTBiNzQzOGRkNzU1ZTRlZWJjZmMyYWVmM2VjOTkwOTZlYjU3
15
+ YzYyYjNkYjdhYzNhNTJmYjcyYzY1MGQyYzE1YjE2OWViMmI4ZjM=
data/README.md CHANGED
@@ -670,6 +670,23 @@ end
670
670
  # GET /users?role=admin&active=1
671
671
  ```
672
672
 
673
+ A neat trick you can do with scopes is interact with complex paths.
674
+
675
+ ```ruby
676
+ class User
677
+ include Her::Model
678
+
679
+ collection_path "organizations/:organization_id/users"
680
+ scope :for_organization, -> { |id| where(organization_id: id) }
681
+ end
682
+
683
+ @user = User.for_organization(3).find(2)
684
+ # GET /organizations/3/users/2
685
+
686
+ @user = User.for_organization(3).create(fullname: "Tobias Fünke")
687
+ # POST /organizations/3 with `fullname=Tobias+Fünke`
688
+ ```
689
+
673
690
  ### Multiple APIs
674
691
 
675
692
  It is possible to use different APIs for different models. Instead of calling `Her::API.setup`, you can create instances of `Her::API`:
@@ -834,7 +851,7 @@ See [CONTRIBUTING.md](https://github.com/remiprev/her/blob/master/CONTRIBUTING.m
834
851
 
835
852
  ### Contributors
836
853
 
837
- These fine folks helped with Her:
854
+ These [fine folks](https://github.com/remiprev/her/contributors) helped with Her:
838
855
 
839
856
  * [@jfcixmedia](https://github.com/jfcixmedia)
840
857
  * [@EtienneLem](https://github.com/EtienneLem)
@@ -163,6 +163,13 @@ module Her
163
163
  Her::Model::Attributes.initialize_collection(self, parsed_data)
164
164
  end
165
165
 
166
+ # Initialize a new object with the "raw" parsed_data from the parsing middleware
167
+ #
168
+ # @private
169
+ def new_from_parsed_data(parsed_data)
170
+ new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
171
+ end
172
+
166
173
  # Define the attributes that will be used to track dirty attributes and validations
167
174
  #
168
175
  # @param [Array] attributes
@@ -233,7 +240,7 @@ module Her
233
240
  def store_her_data(name, value)
234
241
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
235
242
  if @_her_store_#{name} && value.present?
236
- remove_method @_her_store_#{name}
243
+ remove_method @_her_store_#{name}.to_sym
237
244
  remove_method @_her_store_#{name}.to_s + '='
238
245
  end
239
246
 
@@ -42,11 +42,11 @@ module Her
42
42
  params = to_params
43
43
  self.class.request(to_params.merge(:_method => method, :_path => request_path)) do |parsed_data, response|
44
44
  assign_attributes(self.class.parse(parsed_data[:data])) if parsed_data[:data].any?
45
- self.metadata = parsed_data[:metadata]
46
- self.response_errors = parsed_data[:errors]
47
- self.changed_attributes.clear if self.changed_attributes.present?
45
+ @metadata = parsed_data[:metadata]
46
+ @response_errors = parsed_data[:errors]
48
47
 
49
- return false if !response.success? || self.response_errors.any?
48
+ return false if !response.success? || @response_errors.any?
49
+ self.changed_attributes.clear if self.changed_attributes.present?
50
50
  end
51
51
  end
52
52
  end
@@ -65,8 +65,8 @@ module Her
65
65
  run_callbacks :destroy do
66
66
  self.class.request(:_method => method, :_path => request_path) do |parsed_data, response|
67
67
  assign_attributes(self.class.parse(parsed_data[:data])) if parsed_data[:data].any?
68
- self.metadata = parsed_data[:metadata]
69
- self.response_errors = parsed_data[:errors]
68
+ @metadata = parsed_data[:metadata]
69
+ @response_errors = parsed_data[:errors]
70
70
  @destroyed = true
71
71
  end
72
72
  end
@@ -74,37 +74,6 @@ module Her
74
74
  end
75
75
 
76
76
  module ClassMethods
77
- # Fetch specific resource(s) by their ID
78
- #
79
- # @example
80
- # @user = User.find(1)
81
- # # Fetched via GET "/users/1"
82
- #
83
- # @example
84
- # @users = User.find([1, 2])
85
- # # Fetched via GET "/users/1" and GET "/users/2"
86
- def find(*ids)
87
- params = ids.last.is_a?(Hash) ? ids.pop : {}
88
- results = ids.flatten.compact.uniq.map do |id|
89
- resource = nil
90
- request(params.merge(:_method => method_for(:find), :_path => build_request_path(params.merge(primary_key => id)))) do |parsed_data, response|
91
- if response.success?
92
- resource = new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
93
- resource.run_callbacks :find
94
- else
95
- return nil
96
- end
97
- end
98
- resource
99
- end
100
-
101
- if ids.length > 1 || ids.first.kind_of?(Array)
102
- results
103
- else
104
- results.first
105
- end
106
- end
107
-
108
77
  # Create a new chainable scope
109
78
  #
110
79
  # @example
@@ -152,7 +121,7 @@ module Her
152
121
  end
153
122
 
154
123
  # Delegate the following methods to `scoped`
155
- [:all, :where, :create, :build, :first_or_create, :first_or_initialize].each do |method|
124
+ [:all, :where, :create, :build, :find, :first_or_create, :first_or_initialize].each do |method|
156
125
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
157
126
  def #{method}(*params)
158
127
  scoped.send(#{method.to_sym.inspect}, *params)
@@ -73,6 +73,40 @@ module Her
73
73
  end
74
74
  end
75
75
 
76
+ # Fetch specific resource(s) by their ID
77
+ #
78
+ # @example
79
+ # @user = User.find(1)
80
+ # # Fetched via GET "/users/1"
81
+ #
82
+ # @example
83
+ # @users = User.find([1, 2])
84
+ # # Fetched via GET "/users/1" and GET "/users/2"
85
+ def find(*ids)
86
+ params = @params.merge(ids.last.is_a?(Hash) ? ids.pop : {})
87
+
88
+ results = ids.flatten.compact.uniq.map do |id|
89
+ resource = nil
90
+ request_params = params.merge(
91
+ :_method => @parent.method_for(:find),
92
+ :_path => @parent.build_request_path(params.merge(@parent.primary_key => id))
93
+ )
94
+
95
+ @parent.request(request_params) do |parsed_data, response|
96
+ if response.success?
97
+ resource = @parent.new_from_parsed_data(parsed_data)
98
+ resource.run_callbacks :find
99
+ else
100
+ return nil
101
+ end
102
+ end
103
+
104
+ resource
105
+ end
106
+
107
+ ids.length > 1 || ids.first.kind_of?(Array) ? results : results.first
108
+ end
109
+
76
110
  # Create a resource and return it
77
111
  #
78
112
  # @example
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.6.4"
2
+ VERSION = "0.6.5"
3
3
  end
@@ -139,6 +139,13 @@ describe Her::Model::Attributes do
139
139
 
140
140
  context "handling metadata and errors" do
141
141
  before do
142
+ Her::API.setup :url => "https://api.example.com" do |builder|
143
+ builder.use Her::Middleware::FirstLevelParseJSON
144
+ builder.adapter :test do |stub|
145
+ stub.post("/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
146
+ end
147
+ end
148
+
142
149
  spawn_model 'Foo::User' do
143
150
  store_response_errors :errors
144
151
  store_metadata :my_data
@@ -162,6 +169,14 @@ describe Her::Model::Attributes do
162
169
  it "should remove the default method for metadata" do
163
170
  expect { @user.metadata }.to raise_error(NoMethodError)
164
171
  end
172
+
173
+ it "should work with #save" do
174
+ @user.assign_attributes(:fullname => "Tobias Fünke")
175
+ @user.save
176
+ expect { @user.metadata }.to raise_error(NoMethodError)
177
+ @user.my_data.should be_empty
178
+ @user.errors.should be_empty
179
+ end
165
180
  end
166
181
 
167
182
  context "overwriting default attribute methods" do
@@ -9,7 +9,9 @@ describe "Her::Model and ActiveModel::Dirty" do
9
9
  builder.use Faraday::Request::UrlEncoded
10
10
  builder.adapter :test do |stub|
11
11
  stub.get("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Lindsay Fünke" }.to_json] }
12
+ stub.get("/users/2") { |env| [200, {}, { :id => 2, :fullname => "Maeby Fünke" }.to_json] }
12
13
  stub.put("/users/1") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
14
+ stub.put("/users/2") { |env| [400, {}, { :errors => ["Email cannot be blank"] }.to_json] }
13
15
  stub.post("/users") { |env| [200, {}, { :id => 1, :fullname => "Tobias Fünke" }.to_json] }
14
16
  end
15
17
  end
@@ -20,14 +22,28 @@ describe "Her::Model and ActiveModel::Dirty" do
20
22
  end
21
23
 
22
24
  context "for existing resource" do
23
- it "tracks dirty attributes" do
24
- user = Foo::User.find(1)
25
- user.fullname = "Tobias Fünke"
26
- user.fullname_changed?.should be_true
27
- user.email_changed?.should be_false
28
- user.should be_changed
29
- user.save
30
- user.should_not be_changed
25
+ context "with successful save" do
26
+ it "tracks dirty attributes" do
27
+ user = Foo::User.find(1)
28
+ user.fullname = "Tobias Fünke"
29
+ user.fullname_changed?.should be_true
30
+ user.email_changed?.should be_false
31
+ user.should be_changed
32
+ user.save
33
+ user.should_not be_changed
34
+ end
35
+ end
36
+
37
+ context "with erroneous save" do
38
+ it "tracks dirty attributes" do
39
+ user = Foo::User.find(2)
40
+ user.fullname = "Tobias Fünke"
41
+ user.fullname_changed?.should be_true
42
+ user.email_changed?.should be_false
43
+ user.should be_changed
44
+ user.save
45
+ user.should be_changed
46
+ end
31
47
  end
32
48
  end
33
49
 
@@ -190,6 +190,13 @@ describe Her::Model::Paths do
190
190
  @user.id.should == 1
191
191
  @user.fullname.should == "Tobias Fünke"
192
192
  end
193
+
194
+ it "maps a single resource using a scope to a Ruby object" do
195
+ Foo::User.scope :for_organization, lambda { |o| where(:organization_id => o) }
196
+ @user = Foo::User.for_organization(2).find(1)
197
+ @user.id.should == 1
198
+ @user.fullname.should == "Tobias Fünke"
199
+ end
193
200
  end
194
201
 
195
202
  describe "fetching a collection" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: her
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.6.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rémi Prévost
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-04-26 00:00:00.000000000 Z
11
+ date: 2013-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake