api-model 1.1.2 → 2.0.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: d62e3abae3a9c3844eeaa606a37263f57d096057
4
- data.tar.gz: 1cc5a013afbc09fb71f12cded4e9ed6aeb8f5be6
3
+ metadata.gz: 6909e3c913be60c743cfef5babb4dadbe0852ece
4
+ data.tar.gz: db89536d66181112df8fbf7e5ab0589f37f3c56b
5
5
  SHA512:
6
- metadata.gz: ebd2bc0b6ff25732cfac277f91888c908073970bce8eec68be7c59e7b3edc20ccb3abba66e8bc1deb8b0a7c398c0f84f704bf2c6e0e889be3df991828a9d58e2
7
- data.tar.gz: 3b2df71ea9b2fddc4c220f4c4281c62bd2c45c8ac10fc2f22d92e96972287120a27cd078c8cd79e202c9820c32400de143767d6b398ff8f643fed90a55d75a1b
6
+ metadata.gz: 2ae38b790d32233392a537d88cd152f39332e69fa46ae160fb1b40ac9397d8178ed379f3e459650c8a09d56ca6af61f5b1ec9e79273d5ea8f352aad23e0121f8
7
+ data.tar.gz: 8c2a47a297bf712bfc3e782b55b342d9de0d311d6a9f6128358c845ba0a6678d8b12487211f4010b7829e264b9059c7891112b92fa6a3da573b837c1e515af8a
data/Gemfile.lock CHANGED
@@ -1,44 +1,50 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- api-model (1.1.2)
5
- activemodel (~> 4.0)
6
- activesupport (~> 4.0)
4
+ api-model (2.0.0)
5
+ activemodel (~> 4.1)
6
+ activesupport (~> 4.1)
7
7
  hash-pipe (~> 0.0)
8
- hashie (~> 2.0)
9
8
  typhoeus (~> 0.6)
9
+ virtus (~> 1.0)
10
10
 
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- activemodel (4.0.3)
15
- activesupport (= 4.0.3)
16
- builder (~> 3.1.0)
17
- activesupport (4.0.3)
18
- i18n (~> 0.6, >= 0.6.4)
19
- minitest (~> 4.2)
20
- multi_json (~> 1.3)
14
+ activemodel (4.1.0)
15
+ activesupport (= 4.1.0)
16
+ builder (~> 3.1)
17
+ activesupport (4.1.0)
18
+ i18n (~> 0.6, >= 0.6.9)
19
+ json (~> 1.7, >= 1.7.7)
20
+ minitest (~> 5.1)
21
21
  thread_safe (~> 0.1)
22
- tzinfo (~> 0.3.37)
22
+ tzinfo (~> 1.1)
23
23
  addressable (2.3.5)
24
- atomic (1.1.15)
25
- builder (3.1.4)
24
+ axiom-types (0.1.1)
25
+ descendants_tracker (~> 0.0.4)
26
+ ice_nine (~> 0.11.0)
27
+ thread_safe (~> 0.3, >= 0.3.1)
28
+ builder (3.2.2)
26
29
  coderay (1.0.9)
30
+ coercible (1.0.0)
31
+ descendants_tracker (~> 0.0.1)
27
32
  crack (0.4.1)
28
33
  safe_yaml (~> 0.9.0)
34
+ descendants_tracker (0.0.4)
35
+ thread_safe (~> 0.3, >= 0.3.1)
29
36
  diff-lcs (1.2.5)
30
- ethon (0.6.3)
37
+ equalizer (0.0.9)
38
+ ethon (0.7.0)
31
39
  ffi (>= 1.3.0)
32
- mime-types (~> 1.18)
33
40
  ffi (1.9.3)
34
41
  hash-pipe (0.0.1)
35
42
  activesupport (~> 4.0)
36
- hashie (2.0.5)
37
43
  i18n (0.6.9)
44
+ ice_nine (0.11.0)
45
+ json (1.8.1)
38
46
  method_source (0.8.2)
39
- mime-types (1.25.1)
40
- minitest (4.7.5)
41
- multi_json (1.8.4)
47
+ minitest (5.3.2)
42
48
  pry (0.9.12.2)
43
49
  coderay (~> 1.0.5)
44
50
  method_source (~> 0.8)
@@ -53,12 +59,17 @@ GEM
53
59
  rspec-mocks (2.14.4)
54
60
  safe_yaml (0.9.7)
55
61
  slop (3.4.6)
56
- thread_safe (0.2.0)
57
- atomic (>= 1.1.7, < 2)
58
- typhoeus (0.6.7)
59
- ethon (~> 0.6.2)
60
- tzinfo (0.3.38)
62
+ thread_safe (0.3.3)
63
+ typhoeus (0.6.8)
64
+ ethon (>= 0.7.0)
65
+ tzinfo (1.1.0)
66
+ thread_safe (~> 0.1)
61
67
  vcr (2.8.0)
68
+ virtus (1.0.2)
69
+ axiom-types (~> 0.1)
70
+ coercible (~> 1.0)
71
+ descendants_tracker (~> 0.0.3)
72
+ equalizer (~> 0.0.9)
62
73
  webmock (1.15.0)
63
74
  addressable (>= 2.2.7)
64
75
  crack (>= 0.3.2)
data/README.md CHANGED
@@ -12,11 +12,11 @@ A really simple example
12
12
 
13
13
  To turn any class into an API model, it must inherit ApiModel::Base. If you want to
14
14
  make attributes which will get automatically set from api responses, you can define them
15
- as properties..
15
+ as attributes..
16
16
 
17
17
  ``` ruby
18
18
  class MyModel < ApiModel::Base
19
- property :name
19
+ attribute :name, String
20
20
  end
21
21
  ```
22
22
 
@@ -47,46 +47,27 @@ or you can send other request types:
47
47
  call_api :put, url, options
48
48
  ```
49
49
 
50
- Model properties
50
+ Model attributes
51
51
  ----------------
52
52
 
53
- The properties which you can define on models are extended from the [Hashie](https://github.com/intridea/hashie#trash)
54
- gem. You can use them to define simple attributes, but also for converting attributes from one name to another, or for
55
- transforming the values as they are set. This is useful for dealing with APIs which use a different naming scheme
56
- than you are using, or if you need to modify values as they come in.
53
+ The attributes which you can define on models are included from the [Virtus](https://github.com/solnic/virtus)
54
+ gem. You can use them to define simple attributes, coercing values as they come in, or just type-casting. Be sure
55
+ to check out the Virtus docs for more info on what can be achieved.
57
56
 
58
- ### Translation
57
+ To make it easier to work with APIs which have different naming schemes from your models, you can define attribute
58
+ synonyms, which are really just simple aliases.
59
59
 
60
- ```ruby
61
- class MyModel < ApiModel::Base
62
- property :full_name, from: :fullName
63
- end
64
-
65
- MyModel.new(fullName: "Hello").full_name # => "Hello"
66
- ```
67
-
68
- ### Transformation
69
-
70
- ```ruby
71
- class MyModel < ApiModel::Base
72
- property :created_at, from: :timestamp, with: lambda { |t| Time.at(t) }
73
- end
74
-
75
- MyModel.new(timestamp: 1387550991).created_at # => 2013-12-20 15:49:51 +0100
76
- ```
77
-
78
- ### Defaults
60
+ For example, say you have a `Car` model which has a `number_of_wheels` attribute but with the APIs you're using, sometimes
61
+ it the attribute is named `numberOfWheels`, sometimes it's `nrOfWheels` and sometimes it's `wheel_count`, you can easily handle
62
+ them all at once:
79
63
 
80
64
  ```ruby
81
- class MyModel < ApiModel::Base
82
- property :name, default: "FooBar"
65
+ class Car < ApiModel::Base
66
+ attribute :number_of_wheels, Integer
67
+ attribute_synonym :number_of_wheels, :numberOfWheels, :nrOfWheels, :wheel_count
83
68
  end
84
-
85
- MyModel.new.name # => "FooBar"
86
69
  ```
87
70
 
88
- For more information, check out the [Hashie::Trash docs](https://github.com/intridea/hashie#trash).
89
-
90
71
  Building objects from responses
91
72
  -------------------------------
92
73
 
@@ -124,7 +105,7 @@ ActiveModel validations, you can do that, too:
124
105
 
125
106
  ```ruby
126
107
  class Car
127
- property :name
108
+ attribute :name, String
128
109
  end
129
110
 
130
111
  car = Car.new
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 = "1.1.2"
5
+ s.version = "2.0.0"
6
6
  s.authors = ["Damien Timewell"]
7
7
  s.email = ["mail@damientimewell.com"]
8
8
  s.licenses = ['MIT']
@@ -10,10 +10,10 @@ Gem::Specification.new do |s|
10
10
  s.summary = "A simple way of interacting with rest APIs"
11
11
  s.description = "API model is a simple wrapper for interacting with external APIs. It tries to make it very simple and easy to make API calls and map the responses into objects."
12
12
 
13
- s.add_dependency 'activesupport', '~> 4.0'
14
- s.add_dependency 'activemodel', '~> 4.0'
13
+ s.add_dependency 'activesupport', '~> 4.1'
14
+ s.add_dependency 'activemodel', '~> 4.1'
15
15
  s.add_dependency 'typhoeus', '~> 0.6'
16
- s.add_dependency 'hashie', '~> 2.0'
16
+ s.add_dependency 'virtus', '~> 1.0'
17
17
  s.add_dependency 'hash-pipe', '~> 0.0'
18
18
 
19
19
  s.add_development_dependency "rspec", '~> 2.14'
data/lib/api-model.rb CHANGED
@@ -2,14 +2,11 @@ require 'active_model'
2
2
  require 'active_support'
3
3
  require 'active_support/core_ext'
4
4
  require 'logger'
5
- require 'hashie'
5
+ require 'virtus'
6
6
  require 'typhoeus'
7
7
  require 'ostruct'
8
8
  require 'hash-pipe'
9
9
 
10
- require 'api_model/core_extensions/hash'
11
- require 'api_model/core_extensions/hashie'
12
-
13
10
  require 'api_model/assignment'
14
11
  require 'api_model/initializer'
15
12
  require 'api_model/http_request'
@@ -38,7 +35,9 @@ module ApiModel
38
35
  end
39
36
  end
40
37
 
41
- class Base < Hashie::Trash
38
+ class Base
39
+ include Virtus.model
40
+
42
41
  include ActiveModel::Conversion
43
42
  include ActiveModel::Validations
44
43
  include ActiveModel::Serialization
@@ -1,6 +1,13 @@
1
1
  module ApiModel
2
2
  module ClassMethods
3
3
 
4
+ # TODO - try to think of a more memorable name for this...
5
+ def attribute_synonym(primary_method_name, *alternate_names)
6
+ alternate_names.each do |alternate_name|
7
+ alias_method "#{alternate_name}=".to_sym, "#{primary_method_name}=".to_sym
8
+ end
9
+ end
10
+
4
11
  def get_json(path, params={}, options={})
5
12
  call_api :get, path, options.merge(params: params)
6
13
  end
@@ -6,18 +6,10 @@ module ApiModel
6
6
  extend ActiveModel::Callbacks
7
7
  define_model_callbacks :save, :successful_save, :unsuccessful_save
8
8
 
9
- property :persisted, default: false
9
+ attribute :persisted, Axiom::Types::Boolean, default: false
10
10
  alias_method :persisted?, :persisted
11
11
  end
12
12
 
13
- # Overrides Hashie::Trash to catch errors from trying to set properties which have not been defined
14
- # and defines it automatically
15
- def property_exists?(property_name)
16
- super property_name
17
- rescue NoMethodError
18
- Log.debug "Could not set #{property_name} on #{self.class.name}"
19
- end
20
-
21
13
  # Convenience method to handle error hashes and set them as ActiveModel errors on instances.
22
14
  # Using the `obj`, you can move the errors on to child classes if needed.
23
15
  def set_errors_from_hash(errors_hash, obj = self)
@@ -70,11 +62,11 @@ module ApiModel
70
62
  end
71
63
  end
72
64
 
73
- # Returns all the defined properties in a hash without the :persisted property which was added automatically.
65
+ # Returns all the defined attributes in a hash without the :persisted attribute which was added automatically.
74
66
  #
75
67
  # This is useful for when you need to pass the entire object back to an API, or if you want to serialize the object.
76
68
  def properties_hash
77
- self.to_hash.only(*self.class.properties.to_a).except(:persisted).with_indifferent_access
69
+ self.to_hash.only(*self.class.attribute_set.collect(&:name)).except(:persisted).with_indifferent_access
78
70
  end
79
71
 
80
72
  end
@@ -65,7 +65,7 @@ describe ApiModel do
65
65
  end
66
66
  end
67
67
 
68
- describe "using Hashie to build with properties" do
68
+ describe "using Virtus to build with attribute coercion" do
69
69
  describe "with a single object response" do
70
70
  let :car do
71
71
  VCR.use_cassette('cars') { Car.get_json "http://cars.com/one_convertable" }
@@ -121,6 +121,23 @@ describe ApiModel do
121
121
  end
122
122
  end
123
123
 
124
+ describe "defining attribute synonyms" do
125
+ let(:car) { Car.new }
126
+
127
+ it 'should have defined method aliases for numberOfDoors and nrOfDoors' do
128
+ car.numberOfDoors = 10
129
+ car.number_of_doors.should eq 10
130
+
131
+ car.nrOfDoors = 20
132
+ car.number_of_doors.should eq 20
133
+ end
134
+
135
+ it 'should still use Virtus coersion when using an alias' do
136
+ car.max_speed = 10
137
+ car.top_speed.should eq 100 # the coersion is doing * 10
138
+ end
139
+ end
140
+
124
141
  describe "setting errors from a hash" do
125
142
  let(:car) { Car.new }
126
143
  let(:blog_post) { BlogPost.new }
@@ -281,12 +298,6 @@ describe ApiModel do
281
298
  end
282
299
  end
283
300
 
284
- describe "class equality" do
285
- it 'should not be equal to a Hash even though it is technically a Hash subclass' do
286
- (Hash === BlogPost.new).should be_false
287
- end
288
- end
289
-
290
301
  describe "properties_hash" do
291
302
  let(:blog_post) { BlogPost.new title: "Foo", name: "Bar", something_else: "Baz" }
292
303
 
@@ -303,7 +314,7 @@ describe ApiModel do
303
314
  blog_post.properties_hash.should_not have_key(:something_else)
304
315
  end
305
316
 
306
- it 'should not include the :persisted property, even though it is defined' do
317
+ it 'should not include the :persisted attribute, even though it is defined' do
307
318
  blog_post.properties_hash.should_not have_key(:persisted)
308
319
  end
309
320
  end
@@ -1,5 +1,5 @@
1
1
  class Banana < ApiModel::Base
2
- property :color
3
- property :size
4
- property :ripe
2
+ attribute :color, String
3
+ attribute :size, String
4
+ attribute :ripe, Boolean
5
5
  end
@@ -1,6 +1,6 @@
1
1
  class BlogPost < ApiModel::Base
2
- property :name
3
- property :title
2
+ attribute :name, String
3
+ attribute :title, String
4
4
 
5
5
  class CustomBuilder
6
6
  def build(hash)
@@ -1,8 +1,16 @@
1
+ class CarTopSpeed < Virtus::Attribute
2
+ def coerce(value)
3
+ value * 10 if value
4
+ end
5
+ end
6
+
1
7
  class Car < ApiModel::Base
8
+ attribute :number_of_doors, Integer
9
+ attribute :top_speed, CarTopSpeed
10
+ attribute :name, String, default: "Ferrari"
2
11
 
3
- property :number_of_doors, from: :numberOfDoors
4
- property :top_speed, transform_with: lambda { |speed| speed * 10 }
5
- property :name, default: "Ferrari"
12
+ attribute_synonym :number_of_doors, :numberOfDoors, :nrOfDoors
13
+ attribute_synonym :top_speed, :max_speed
6
14
 
7
15
  def is_fast?
8
16
  top_speed > 300
@@ -1,4 +1,4 @@
1
1
  class User < ApiModel::Base
2
- property :name
3
- property :email_address
2
+ attribute :name, String
3
+ attribute :email_address, String
4
4
  end
metadata CHANGED
@@ -1,111 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 2.0.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: 2014-03-05 00:00:00.000000000 Z
11
+ date: 2014-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '4.0'
19
+ version: '4.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '4.0'
26
+ version: '4.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '4.0'
33
+ version: '4.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '4.0'
40
+ version: '4.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: typhoeus
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0.6'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0.6'
55
55
  - !ruby/object:Gem::Dependency
56
- name: hashie
56
+ name: virtus
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ~>
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '2.0'
61
+ version: '1.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '2.0'
68
+ version: '1.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: hash-pipe
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ~>
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0.0'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ~>
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rspec
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ~>
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
89
  version: '2.14'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ~>
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '2.14'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: pry
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ~>
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0.9'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ~>
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0.9'
111
111
  - !ruby/object:Gem::Dependency
@@ -145,9 +145,9 @@ executables: []
145
145
  extensions: []
146
146
  extra_rdoc_files: []
147
147
  files:
148
- - .gitignore
149
- - .rspec
150
- - .travis.yml
148
+ - ".gitignore"
149
+ - ".rspec"
150
+ - ".travis.yml"
151
151
  - Gemfile
152
152
  - Gemfile.lock
153
153
  - README.md
@@ -158,8 +158,6 @@ files:
158
158
  - lib/api_model/cache_stategy/no_cache.rb
159
159
  - lib/api_model/class_methods.rb
160
160
  - lib/api_model/configuration.rb
161
- - lib/api_model/core_extensions/hash.rb
162
- - lib/api_model/core_extensions/hashie.rb
163
161
  - lib/api_model/http_request.rb
164
162
  - lib/api_model/initializer.rb
165
163
  - lib/api_model/instance_methods.rb
@@ -191,12 +189,12 @@ require_paths:
191
189
  - lib
192
190
  required_ruby_version: !ruby/object:Gem::Requirement
193
191
  requirements:
194
- - - '>='
192
+ - - ">="
195
193
  - !ruby/object:Gem::Version
196
194
  version: '0'
197
195
  required_rubygems_version: !ruby/object:Gem::Requirement
198
196
  requirements:
199
- - - '>='
197
+ - - ">="
200
198
  - !ruby/object:Gem::Version
201
199
  version: '0'
202
200
  requirements: []
@@ -1,16 +0,0 @@
1
- # When using ApiModel in a Rails application, Rails will try to ascertain what type of class
2
- # an instance of ApiModel::Base is when attempting to build a polymorphic route. Since ApiModel::Base
3
- # inherits from Hashie, which in turn inherits from Hash, Rails thinks that an instance of
4
- # ApiModel::Base is in fact a hash instead of acting like an ActiveModel, which then causes
5
- # it to fail to compute a route for the class.
6
- #
7
- # This is a hacky workaround, but should not interfere with any core functionality since it just
8
- # calls super if the class is not a subclass of ApiModel::Base.
9
- Hash.class_eval do
10
-
11
- def self.===(klass)
12
- return false if klass.class.ancestors.include? ApiModel::Base
13
- super klass
14
- end
15
-
16
- end
@@ -1,8 +0,0 @@
1
- Hashie::Dash.class_eval do
2
-
3
- # Prevent hashie from raising errors when trying to set properties which have not been defined.
4
- def assert_property_exists!(property)
5
- super rescue NoMethodError
6
- end
7
-
8
- end