api-model 0.0.4 → 0.1.0

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,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fd7712e1eb0b5faac320b38518575bfbdeaf61b8
4
- data.tar.gz: 2ea94f183ad7de875863ff989327dc414d2dc54a
3
+ metadata.gz: b4468bee16f23aca5211838b3f54b918a0340716
4
+ data.tar.gz: 27a2004bc4145a4e6c71e3ae87c703b092b0c7d3
5
5
  SHA512:
6
- metadata.gz: f1e74e2574025ccf61e563ca0631e855211ff6953cb36a2d4986ef8c301c8477cda1913421c66d103dc225db2f10653693feec8706cbbd989dae3a60e351f585
7
- data.tar.gz: 68171597b2cb8448a10237485904d3353012196cc911d6c1cf09648e0e0e1d7f84bb5957e29ce51b4648fd995cd095bf9467c70ff26cae40ff1d385be17c4eae
6
+ metadata.gz: dcfae89dae5eb342dd14c6992efdd94cb2bff77582d2ede112bc3446a129225a1dd0025c4783bb7425bbe95c04518eca6e8f5d53d3a7bc2d177b994adfb45eeb
7
+ data.tar.gz: 6028927509a5e087babd8ad3f01d37819928a2bc39651c49a16c862bb77ea9b85c861d14560faf6f62d7b74fd69c3ec04cc04d76b35923958386c352488387cf
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- api-model (0.0.4)
4
+ api-model (0.1.0)
5
5
  activemodel
6
6
  activesupport
7
7
  hashie
data/README.md CHANGED
@@ -30,7 +30,7 @@ Then, let's say the API endpoint /foo returned JSON which looks like `{ "name":
30
30
  Request types and params
31
31
  ------------------------
32
32
 
33
- There's a couple of convenience methods to make it simpler to send GET and POST requests,
33
+ There's a couple of convenience methods to make it simpler to send GET and POST requests,
34
34
  or you can send other request types:
35
35
 
36
36
  ```ruby
@@ -114,11 +114,29 @@ use as a builder should respond to `#build`, with the instance hash as an argume
114
114
  MyModel.get_json "/foo", { some_param: "bar" }, builder: MyCustomBuilder.new
115
115
  ```
116
116
 
117
+ Handling validation errors in responses
118
+ ---------------------------------------
119
+
120
+ ApiModel uses a bunch of Rails' ActiveModel enhancements to make it easy to use things such as validation errors.
121
+ You can define validations in the normal ActiveModel::Validations style and check validity before posting
122
+ to external APIs should you wish to. Or, if an external API returns errors which you would like to convert to
123
+ ActiveModel validations, you can do that, too:
124
+
125
+ ```ruby
126
+ class Car
127
+ property :name
128
+ end
129
+
130
+ car = Car.new
131
+ car.set_errors_from_hash name: "cannot be blank"
132
+ car.errors[:name] # => ["cannot be blank"]
133
+ ```
134
+
117
135
  Configuring API Model
118
136
  ---------------------
119
137
 
120
138
  You can configure API model in a number of places; globally using `ApiModel::Base.api_config`, per-model
121
- using `MyModel.api_config`, and per-api call by passing in options in the options hash (although some
139
+ using `MyModel.api_config`, and per-api call by passing in options in the options hash (although some
122
140
  configuration options may not be available on the per-api call technique).
123
141
 
124
142
  ### API Host
@@ -141,7 +159,7 @@ to refer to the full url all the time.
141
159
  ```
142
160
 
143
161
  If the API response which you receive is deeply nested and you want to cut out some levels of nesting, you
144
- can use `json_root` to set which key objects should be built from.
162
+ can use `json_root` to set which key objects should be built from.
145
163
 
146
164
  You can dig down multiple levels by separating keys with a period. With the example above, say the server
147
165
  was returning JSON which looked like `{"data":{"posts":{"name":"Foo"}}}`, it would behave as if the
@@ -201,12 +219,12 @@ something like this:
201
219
  ```ruby
202
220
  class MyCustomCacheStrategy
203
221
  attr_accessor :id, :options
204
-
222
+
205
223
  def initialize(id, options)
206
224
  @id = id
207
225
  @options = options
208
226
  end
209
-
227
+
210
228
  def cache(&block)
211
229
  # here you can check whether you want to actually call the api by running
212
230
  # block.call, or want to find and return your cached response.
data/api-model.gemspec CHANGED
@@ -2,7 +2,7 @@ $:.push File.expand_path("../lib", __FILE__)
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "api-model"
5
- s.version = "0.0.4"
5
+ s.version = "0.1.0"
6
6
  s.authors = ["Damien Timewell"]
7
7
  s.email = ["mail@damientimewell.com"]
8
8
  s.homepage = "https://github.com/iZettle/api-model"
data/lib/api-model.rb CHANGED
@@ -8,7 +8,8 @@ require 'ostruct'
8
8
  require 'api_model/initializer'
9
9
  require 'api_model/http_request'
10
10
  require 'api_model/response'
11
- require 'api_model/rest_methods'
11
+ require 'api_model/class_methods'
12
+ require 'api_model/instance_methods'
12
13
  require 'api_model/configuration'
13
14
  require 'api_model/cache_stategies/no_cache'
14
15
  require 'api_model/response_parser/json'
@@ -36,17 +37,9 @@ module ApiModel
36
37
  extend ActiveModel::Naming
37
38
  extend ActiveModel::Callbacks
38
39
 
39
- extend RestMethods
40
+ extend ClassMethods
40
41
  include ConfigurationMethods
41
-
42
- # Overrides Hashie::Trash to catch errors from trying to set properties which have not been defined
43
- # and defines it automatically
44
- def property_exists?(property_name)
45
- super property_name
46
- rescue NoMethodError
47
- Log.debug "Could not set #{property_name} on #{self.class.name}. Defining it now."
48
- self.class.property property_name.to_sym
49
- end
42
+ include InstanceMethods
50
43
  end
51
44
 
52
45
  end
@@ -1,5 +1,5 @@
1
1
  module ApiModel
2
- module RestMethods
2
+ module ClassMethods
3
3
 
4
4
  def get_json(path, params={}, options={})
5
5
  call_api :get, path, options.merge(params: params)
@@ -0,0 +1,28 @@
1
+ module ApiModel
2
+ module InstanceMethods
3
+
4
+ # Overrides Hashie::Trash to catch errors from trying to set properties which have not been defined
5
+ # and defines it automatically
6
+ def property_exists?(property_name)
7
+ super property_name
8
+ rescue NoMethodError
9
+ Log.debug "Could not set #{property_name} on #{self.class.name}. Defining it now."
10
+ self.class.property property_name.to_sym
11
+ end
12
+
13
+ # Convenience method to handle error hashes and set them as ActiveModel errors on instances.
14
+ # Using the `obj`, you can move the errors on to child classes if needed.
15
+ def set_errors_from_hash(errors_hash, obj = self)
16
+ errors_hash.each do |field,messages|
17
+ if messages.is_a?(Array)
18
+ messages.each do |message|
19
+ obj.errors.add field.to_sym, message
20
+ end
21
+ else
22
+ obj.errors.add field.to_sym, messages
23
+ end
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -127,6 +127,27 @@ describe ApiModel do
127
127
  end
128
128
  end
129
129
 
130
+ describe "setting errors from a hash" do
131
+ let(:car) { Car.new }
132
+ let(:blog_post) { BlogPost.new }
133
+
134
+ it 'should assign errors from a simple hash using active model errors' do
135
+ car.set_errors_from_hash name: "Is invalid"
136
+ car.errors[:name].should eq ["Is invalid"]
137
+ end
138
+
139
+ it 'should assign multiple errors from an array' do
140
+ car.set_errors_from_hash top_speed: ["is too fast", "would break the sound barrier"]
141
+ car.errors[:top_speed].size.should eq 2
142
+ end
143
+
144
+ it 'should be possible to assign the errors to other classes' do
145
+ car.set_errors_from_hash({ name: "is bad" }, blog_post)
146
+ car.errors.size.should eq 0
147
+ blog_post.errors[:name].should eq ["is bad"]
148
+ end
149
+ end
150
+
130
151
  describe "cache_id" do
131
152
  it 'should use options and the request path to create an identifier for the cache' do
132
153
  BlogPost.cache_id("/box", params: { foo: "bar" }).should eq "/boxfoobar"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Damien Timewell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-21 00:00:00.000000000 Z
11
+ date: 2014-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -140,12 +140,13 @@ files:
140
140
  - api-model.gemspec
141
141
  - lib/api-model.rb
142
142
  - lib/api_model/cache_stategies/no_cache.rb
143
+ - lib/api_model/class_methods.rb
143
144
  - lib/api_model/configuration.rb
144
145
  - lib/api_model/http_request.rb
145
146
  - lib/api_model/initializer.rb
147
+ - lib/api_model/instance_methods.rb
146
148
  - lib/api_model/response.rb
147
149
  - lib/api_model/response_parser/json.rb
148
- - lib/api_model/rest_methods.rb
149
150
  - spec/api-model/api_model_spec.rb
150
151
  - spec/api-model/configuration_spec.rb
151
152
  - spec/api-model/http_request_spec.rb