her 0.7.6 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1640cf3b6da7256158a07bd83d34cde38de4e3b7
4
- data.tar.gz: f98a7292913c5227d8843e18220f4df6023f688d
3
+ metadata.gz: 606f0ea519a2ac25fd041636d9ef3294a26b406d
4
+ data.tar.gz: 86d7b4be4bdba0ce5e93b6410c53323478de6c7b
5
5
  SHA512:
6
- metadata.gz: f509b4f473ea0fb74b7f4fa7ad939bb8b7ea9fa9b50d73847f56a5192878f3d3f2e96995b31ae20d5479e2c4603e9e5073d8ade4100248950fc96179e00f4ef3
7
- data.tar.gz: 22874bb7b1a909f226e85821ab99d77b7b8cb79f6265b0699ba017497d3fde9753c54905aaabae41de95976e86b90ada01a7632251bc24b1d4b6fd3bea7c05bb
6
+ metadata.gz: 177aa0aa91c294a4300ce3aed89646499df76d5b82f3230a9ce4b0e46e037ad4c2086ddb60a31d9ebb502845c26e75b6fcce069f2fb4a7166f18a3b9a60c6e48
7
+ data.tar.gz: 293fdbad4a95fc6d3126b4292b2de8624495751e2e12f4c272a0070ac41d2d832b5842e2b263cc118fd50d84f893e9cd4e9b14f51ee45edf1757ef4a74a21659
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-2013 Rémi Prévost
1
+ Copyright (c) 2012-2015 Rémi Prévost
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
data/README.md CHANGED
@@ -586,20 +586,44 @@ users = Users.all
586
586
 
587
587
  #### JSON API support
588
588
 
589
- If the API returns data in the [JSON API format](http://jsonapi.org/) you need
590
- to configure Her as follows:
589
+ To consume a JSON API 1.0 compliant service, it must return data in accordance with the [JSON API spec](http://jsonapi.org/). The general format
590
+ of the data is as follows:
591
+
592
+ ```json
593
+ { "data": {
594
+ "type": "developers",
595
+ "id": "6ab79c8c-ec5a-4426-ad38-8763bbede5a7",
596
+ "attributes": {
597
+ "language": "ruby",
598
+ "name": "avdi grimm",
599
+ }
600
+ }
601
+ ```
602
+
603
+ Then to setup your models:
591
604
 
592
605
  ```ruby
593
- class User
594
- include Her::Model
595
- parse_root_in_json true, format: :json_api
606
+ class Contributor
607
+ include Her::JsonApi::Model
608
+
609
+ # defaults to demodulized, pluralized class name, e.g. contributors
610
+ type :developers
596
611
  end
612
+ ```
597
613
 
598
- user = Users.find(1)
599
- # GET "/users/1", response is { "users": [{ "id": 1, "fullname": "Lindsay Fünke" }] }
614
+ Finally, you'll need to use the included JsonApiParser Her middleware:
600
615
 
601
- users = Users.all
602
- # GET "/users", response is { "users": [{ "id": 1, "fullname": "Lindsay Fünke" }, { "id": 2, "fullname": "Tobias Fünke" }] }
616
+ ```ruby
617
+ Her::API.setup url: 'https://my_awesome_json_api_service' do |c|
618
+ # Request
619
+ c.use FaradayMiddleware::EncodeJson
620
+
621
+ # Response
622
+ c.use Her::Middleware::JsonApiParser
623
+
624
+ # Adapter
625
+ c.use Faraday::Adapter::NetHttp
626
+ end
603
627
  ```
604
628
 
605
629
  ### Custom requests
data/UPGRADE.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  Here is a list of notable changes by release. Her follows the [Semantic Versioning](http://semver.org/) system.
4
4
 
5
+ ## 0.8.0
6
+
7
+ - Initial support for JSONAPI [link](https://github.com/remiprev/her/pull/347)
8
+ - Fix for has_one association parsing [link](https://github.com/remiprev/her/pull/352)
9
+ - Fix for escaping path variables HT @marshall-lee [link](https://github.com/remiprev/her/pull/354)
10
+ - Fix syntax highlighting in README HT @tippenein [link](https://github.com/remiprev/her/pull/356)
11
+ - Fix associations with Active Model Serializers HT @minktom [link](https://github.com/remiprev/her/pull/359)
12
+
5
13
  ## 0.7.6
6
14
 
7
15
  - Loosen restrictions on ActiveSupport and ActiveModel to accommodate security fixes [link](https://github.com/remiprev/her/commit/8ff641fcdaf14be7cc9b1a6ee6654f27f7dfa34c)
data/lib/her.rb CHANGED
@@ -13,4 +13,7 @@ require "her/errors"
13
13
  require "her/collection"
14
14
 
15
15
  module Her
16
+ module JsonApi
17
+ autoload :Model, 'her/json_api/model'
18
+ end
16
19
  end
data/lib/her/api.rb CHANGED
@@ -3,7 +3,7 @@ module Her
3
3
  # so it knows where to make those requests. In Rails, this is usually done in `config/initializers/her.rb`:
4
4
  class API
5
5
  # @private
6
- attr_reader :base_uri, :connection, :options
6
+ attr_reader :connection, :options
7
7
 
8
8
  # Constants
9
9
  FARADAY_OPTIONS = [:request, :proxy, :ssl, :builder, :url, :parallel_manager, :params, :headers, :builder_class].freeze
@@ -70,7 +70,6 @@ module Her
70
70
  # end
71
71
  def setup(opts={}, &blk)
72
72
  opts[:url] = opts.delete(:base_uri) if opts.include?(:base_uri) # Support legacy :base_uri option
73
- @base_uri = opts[:url]
74
73
  @options = opts
75
74
 
76
75
  faraday_options = @options.reject { |key, value| !FARADAY_OPTIONS.include?(key.to_sym) }
@@ -0,0 +1,46 @@
1
+ module Her
2
+ module JsonApi
3
+ module Model
4
+
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ include Her::Model
8
+
9
+ [:parse_root_in_json, :include_root_in_json, :root_element, :primary_key].each do |method|
10
+ define_method method do |*args|
11
+ raise NoMethodError, "Her::JsonApi::Model does not support the #{method} configuration option"
12
+ end
13
+ end
14
+
15
+ method_for :update, :patch
16
+
17
+ @type = name.demodulize.tableize
18
+
19
+ def self.parse(data)
20
+ data.fetch(:attributes).merge(data.slice(:id))
21
+ end
22
+
23
+ def self.to_params(attributes, changes={})
24
+ request_data = { type: @type }.tap { |request_body|
25
+ attrs = attributes.dup.symbolize_keys.tap { |filtered_attributes|
26
+ if her_api.options[:send_only_modified_attributes]
27
+ filtered_attributes = changes.symbolize_keys.keys.inject({}) do |hash, attribute|
28
+ hash[attribute] = filtered_attributes[attribute]
29
+ hash
30
+ end
31
+ end
32
+ }
33
+ request_body[:id] = attrs.delete(:id) if attrs[:id]
34
+ request_body[:attributes] = attrs
35
+ }
36
+ { data: request_data }
37
+ end
38
+
39
+ def self.type(type_name)
40
+ @type = type_name.to_s
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -6,5 +6,7 @@ require "her/middleware/accept_json"
6
6
  module Her
7
7
  module Middleware
8
8
  DefaultParseJSON = FirstLevelParseJSON
9
+
10
+ autoload :JsonApiParser, 'her/middleware/json_api_parser'
9
11
  end
10
12
  end
@@ -0,0 +1,36 @@
1
+ module Her
2
+ module Middleware
3
+ # This middleware expects the resource/collection data to be contained in the `data`
4
+ # key of the JSON object
5
+ class JsonApiParser < ParseJSON
6
+ # Parse the response body
7
+ #
8
+ # @param [String] body The response body
9
+ # @return [Mixed] the parsed response
10
+ # @private
11
+ def parse(body)
12
+ json = parse_json(body)
13
+
14
+ {
15
+ :data => json[:data] || {},
16
+ :errors => json[:errors] || [],
17
+ :metadata => json[:meta] || {},
18
+ }
19
+ end
20
+
21
+ # This method is triggered when the response has been received. It modifies
22
+ # the value of `env[:body]`.
23
+ #
24
+ # @param [Hash] env The response environment
25
+ # @private
26
+ def on_complete(env)
27
+ env[:body] = case env[:status]
28
+ when 204
29
+ parse('{}')
30
+ else
31
+ parse(env[:body])
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
data/lib/her/model.rb CHANGED
@@ -67,6 +67,9 @@ module Her
67
67
  # Configure ActiveModel callbacks
68
68
  extend ActiveModel::Callbacks
69
69
  define_model_callbacks :create, :update, :save, :find, :destroy, :initialize
70
+
71
+ # Define matchers for attr? and attr= methods
72
+ define_attribute_method_matchers
70
73
  end
71
74
  end
72
75
  end
@@ -29,7 +29,7 @@ module Her
29
29
  if data[data_key].kind_of?(klass)
30
30
  { association[:name] => data[data_key] }
31
31
  else
32
- { association[:name] => klass.new(data[data_key]) }
32
+ { association[:name] => klass.new(klass.parse(data[data_key])) }
33
33
  end
34
34
  end
35
35
 
@@ -94,7 +94,7 @@ module Her
94
94
  def find(id)
95
95
  return nil if id.blank?
96
96
  path = build_association_path lambda { "#{@parent.request_path(@params)}#{@opts[:path]}/#{id}" }
97
- @klass.get(path, @params)
97
+ @klass.get_resource(path, @params)
98
98
  end
99
99
 
100
100
  end
@@ -74,14 +74,14 @@ module Her
74
74
  def fetch
75
75
  foreign_key_value = @parent.attributes[@opts[:foreign_key].to_sym]
76
76
  data_key_value = @parent.attributes[@opts[:data_key].to_sym]
77
- return @opts[:default].try(:dup) if (@parent.attributes.include?(@name) && @parent.attributes[@name].nil? && @params.empty?) || (@parent.persisted? && foreign_key_value.blank? && data_key_value.blank?)
77
+ return @opts[:default].try(:dup) if (@parent.attributes.include?(@name) && @parent.attributes[@name].nil? && @params.empty?) || (foreign_key_value.blank? && data_key_value.blank?)
78
78
 
79
79
  return @cached_result unless @params.any? || @cached_result.nil?
80
80
  return @parent.attributes[@name] unless @params.any? || @parent.attributes[@name].blank?
81
81
 
82
82
  path_params = @parent.attributes.merge(@params.merge(@klass.primary_key => foreign_key_value))
83
83
  path = build_association_path lambda { @klass.build_request_path(path_params) }
84
- @klass.get(path, @params).tap do |result|
84
+ @klass.get_resource(path, @params).tap do |result|
85
85
  @cached_result = result if @params.blank?
86
86
  end
87
87
  end
@@ -158,6 +158,22 @@ module Her
158
158
  @attributes.hash
159
159
  end
160
160
 
161
+ # Assign attribute value (ActiveModel convention method).
162
+ #
163
+ # @private
164
+ def attribute=(attribute, value)
165
+ @attributes[attribute] = nil unless @attributes.include?(attribute)
166
+ self.send(:"#{attribute}_will_change!") if @attributes[attribute] != value
167
+ @attributes[attribute] = value
168
+ end
169
+
170
+ # Check attribute value to be present (ActiveModel convention method).
171
+ #
172
+ # @private
173
+ def attribute?(attribute)
174
+ @attributes.include?(attribute) && @attributes[attribute].present?
175
+ end
176
+
161
177
  module ClassMethods
162
178
  # Initialize a collection of resources with raw data from an HTTP request
163
179
  #
@@ -175,6 +191,25 @@ module Her
175
191
  new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:metadata], :_errors => parsed_data[:errors])
176
192
  end
177
193
 
194
+ # Define attribute method matchers to automatically define them using ActiveModel's define_attribute_methods.
195
+ #
196
+ # @private
197
+ def define_attribute_method_matchers
198
+ attribute_method_suffix '='
199
+ attribute_method_suffix '?'
200
+ end
201
+
202
+ # Create a mutex for dynamically generated attribute methods or use one defined by ActiveModel.
203
+ #
204
+ # @private
205
+ def attribute_methods_mutex
206
+ @attribute_methods_mutex ||= if generated_attribute_methods.respond_to? :mu_synchronize
207
+ generated_attribute_methods
208
+ else
209
+ Mutex.new
210
+ end
211
+ end
212
+
178
213
  # Define the attributes that will be used to track dirty attributes and validations
179
214
  #
180
215
  # @param [Array] attributes
@@ -184,22 +219,8 @@ module Her
184
219
  # attributes :name, :email
185
220
  # end
186
221
  def attributes(*attributes)
187
- define_attribute_methods attributes
188
-
189
- attributes.each do |attribute|
190
- unless method_defined?(:"#{attribute}=")
191
- define_method("#{attribute}=") do |value|
192
- @attributes[:"#{attribute}"] = nil unless @attributes.include?(:"#{attribute}")
193
- self.send(:"#{attribute}_will_change!") if @attributes[:"#{attribute}"] != value
194
- @attributes[:"#{attribute}"] = value
195
- end
196
- end
197
-
198
- unless method_defined?(:"#{attribute}?")
199
- define_method("#{attribute}?") do
200
- @attributes.include?(:"#{attribute}") && @attributes[:"#{attribute}"].present?
201
- end
202
- end
222
+ attribute_methods_mutex.synchronize do
223
+ define_attribute_methods attributes
203
224
  end
204
225
  end
205
226
 
data/lib/her/model/orm.rb CHANGED
@@ -8,6 +8,7 @@ module Her
8
8
  def new?
9
9
  id.nil?
10
10
  end
11
+ alias new_record? new?
11
12
 
12
13
  # Return `true` if a resource is not `#new?`
13
14
  def persisted?
@@ -169,7 +169,7 @@ module Her
169
169
  #
170
170
  # @private
171
171
  def extract_array(request_data)
172
- if active_model_serializers_format? || json_api_format?
172
+ if request_data[:data].is_a?(Hash) && (active_model_serializers_format? || json_api_format?)
173
173
  request_data[:data][pluralized_parsed_root_element]
174
174
  else
175
175
  request_data[:data]
@@ -106,8 +106,13 @@ module Her
106
106
 
107
107
  path.gsub(/:([\w_]+)/) do
108
108
  # Look for :key or :_key, otherwise raise an exception
109
- value = $1.to_sym
110
- parameters.delete(value) || parameters.delete(:"_#{value}") || raise(Her::Errors::PathError.new("Missing :_#{$1} parameter to build the request path. Path is `#{path}`. Parameters are `#{parameters.symbolize_keys.inspect}`.", $1))
109
+ key = $1.to_sym
110
+ value = parameters.delete(key) || parameters.delete(:"_#{key}")
111
+ if value
112
+ Faraday::Utils.escape value
113
+ else
114
+ raise(Her::Errors::PathError.new("Missing :_#{$1} parameter to build the request path. Path is `#{path}`. Parameters are `#{parameters.symbolize_keys.inspect}`.", $1))
115
+ end
111
116
  end
112
117
  end
113
118
 
data/lib/her/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.7.6"
2
+ VERSION = "0.8.1"
3
3
  end
data/spec/api_spec.rb CHANGED
@@ -5,24 +5,7 @@ describe Her::API do
5
5
  subject { Her::API.new }
6
6
 
7
7
  context "initialization" do
8
- describe ".setup" do
9
- it "creates a default connection" do
10
- Her::API.setup :url => "https://api.example.com"
11
- Her::API.default_api.base_uri.should == "https://api.example.com"
12
- end
13
- end
14
-
15
8
  describe "#setup" do
16
- context "when using :url option" do
17
- before { subject.setup :url => "https://api.example.com" }
18
- its(:base_uri) { should == "https://api.example.com" }
19
- end
20
-
21
- context "when using the legacy :base_uri option" do
22
- before { subject.setup :base_uri => "https://api.example.com" }
23
- its(:base_uri) { should == "https://api.example.com" }
24
- end
25
-
26
9
  context "when setting custom middleware" do
27
10
  before do
28
11
  class Foo; end;
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+
3
+ describe Her::JsonApi::Model do
4
+ before do
5
+ Her::API.setup :url => "https://api.example.com" do |connection|
6
+ connection.use Her::Middleware::JsonApiParser
7
+ connection.adapter :test do |stub|
8
+ stub.get("/users/1") do |env|
9
+ [
10
+ 200,
11
+ {},
12
+ {
13
+ data: {
14
+ id: 1,
15
+ type: 'users',
16
+ attributes: {
17
+ name: "Roger Federer",
18
+ },
19
+ }
20
+
21
+ }.to_json
22
+ ]
23
+ end
24
+
25
+ stub.get("/users") do |env|
26
+ [
27
+ 200,
28
+ {},
29
+ {
30
+ data: [
31
+ {
32
+ id: 1,
33
+ type: 'users',
34
+ attributes: {
35
+ name: "Roger Federer",
36
+ },
37
+ },
38
+ {
39
+ id: 2,
40
+ type: 'users',
41
+ attributes: {
42
+ name: "Kei Nishikori",
43
+ },
44
+ }
45
+ ]
46
+ }.to_json
47
+ ]
48
+ end
49
+
50
+ stub.post("/users", data: {
51
+ type: 'users',
52
+ attributes: {
53
+ name: "Jeremy Lin",
54
+ },
55
+ }) do |env|
56
+ [
57
+ 201,
58
+ {},
59
+ {
60
+ data: {
61
+ id: 3,
62
+ type: 'users',
63
+ attributes: {
64
+ name: 'Jeremy Lin',
65
+ },
66
+ }
67
+
68
+ }.to_json
69
+ ]
70
+ end
71
+
72
+ stub.patch("/users/1", data: {
73
+ type: 'users',
74
+ id: 1,
75
+ attributes: {
76
+ name: "Fed GOAT",
77
+ },
78
+ }) do |env|
79
+ [
80
+ 200,
81
+ {},
82
+ {
83
+ data: {
84
+ id: 1,
85
+ type: 'users',
86
+ attributes: {
87
+ name: 'Fed GOAT',
88
+ },
89
+ }
90
+
91
+ }.to_json
92
+ ]
93
+ end
94
+
95
+ stub.delete("/users/1") { |env|
96
+ [ 204, {}, {}, ]
97
+ }
98
+ end
99
+
100
+ end
101
+
102
+ spawn_model("Foo::User", type: Her::JsonApi::Model)
103
+ end
104
+
105
+ it 'allows configuration of type' do
106
+ spawn_model("Foo::Bar", type: Her::JsonApi::Model) do
107
+ type :foobars
108
+ end
109
+
110
+ expect(Foo::Bar.instance_variable_get('@type')).to eql('foobars')
111
+ end
112
+
113
+ it 'finds models by id' do
114
+ user = Foo::User.find(1)
115
+ expect(user.attributes).to eql(
116
+ 'id' => 1,
117
+ 'name' => 'Roger Federer',
118
+ )
119
+ end
120
+
121
+ it 'finds a collection of models' do
122
+ users = Foo::User.all
123
+ expect(users.map(&:attributes)).to match_array([
124
+ {
125
+ 'id' => 1,
126
+ 'name' => 'Roger Federer',
127
+ },
128
+ {
129
+ 'id' => 2,
130
+ 'name' => 'Kei Nishikori',
131
+ }
132
+ ])
133
+ end
134
+
135
+ it 'creates a Foo::User' do
136
+ user = Foo::User.new(name: 'Jeremy Lin')
137
+ user.save
138
+ expect(user.attributes).to eql(
139
+ 'id' => 3,
140
+ 'name' => 'Jeremy Lin',
141
+ )
142
+ end
143
+
144
+ it 'updates a Foo::User' do
145
+ user = Foo::User.find(1)
146
+ user.name = 'Fed GOAT'
147
+ user.save
148
+ expect(user.attributes).to eql(
149
+ 'id' => 1,
150
+ 'name' => 'Fed GOAT',
151
+ )
152
+ end
153
+
154
+ it 'destroys a Foo::User' do
155
+ user = Foo::User.find(1)
156
+ expect(user.destroy).to be_destroyed
157
+ end
158
+
159
+ context 'undefined methods' do
160
+ it 'removes methods that are not compatible with json api' do
161
+ [:parse_root_in_json, :include_root_in_json, :root_element, :primary_key].each do |method|
162
+ expect { Foo::User.new.send(method, :foo) }.to raise_error NoMethodError, "Her::JsonApi::Model does not support the #{method} configuration option"
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe Her::Middleware::JsonApiParser do
5
+ subject { described_class.new }
6
+
7
+ context "with valid JSON body" do
8
+ let(:body) { '{"data": {"type": "foo", "id": "bar", "attributes": {"baz": "qux"} }, "meta": {"api": "json api"} }' }
9
+ let(:env) { { body: body } }
10
+
11
+ it "parses body as json" do
12
+ subject.on_complete(env)
13
+ env.fetch(:body).tap do |json|
14
+ expect(json[:data]).to eql(
15
+ :type => "foo",
16
+ :id => "bar",
17
+ :attributes => { :baz => "qux" }
18
+ )
19
+ expect(json[:errors]).to eql([])
20
+ expect(json[:metadata]).to eql(:api => "json api")
21
+ end
22
+ end
23
+ end
24
+
25
+ #context "with invalid JSON body" do
26
+ # let(:body) { '"foo"' }
27
+ # it 'ensures that invalid JSON throws an exception' do
28
+ # expect { subject.parse(body) }.to raise_error(Her::Errors::ParseError, 'Response from the API must behave like a Hash or an Array (last JSON response was "\"foo\"")')
29
+ # end
30
+ #end
31
+
32
+ end
@@ -76,7 +76,7 @@ describe Her::Model::Associations do
76
76
  describe "associations accessor" do
77
77
  subject { Class.new(Foo::User).associations }
78
78
  its(:object_id) { should_not eql Foo::User.associations.object_id }
79
- its([:has_many]) { should eql [{ :name => :comments, :data_key => :comments, :default => [], :class_name => "Post", :path => "/comments", :inverse_of => nil }] }
79
+ its([:has_many]) { should eql [{ :name => :comments, :data_key => :comments, :default => [], :class_name => "Post", :path => "/comments", :inverse_of => nil }] }
80
80
  end
81
81
  end
82
82
  end
@@ -132,6 +132,7 @@ describe Her::Model::Associations do
132
132
 
133
133
  @user_with_included_data = Foo::User.find(1)
134
134
  @user_without_included_data = Foo::User.find(2)
135
+ @user_without_organization_and_not_persisted = Foo::User.new(organization_id: nil, name: "Katlin Fünke")
135
136
  end
136
137
 
137
138
  let(:user_with_included_data_after_create) { Foo::User.create }
@@ -210,6 +211,10 @@ describe Her::Model::Associations do
210
211
  @user_without_included_data.organization.name.should == "Bluth Company"
211
212
  end
212
213
 
214
+ it "returns nil if the foreign key is nil" do
215
+ @user_without_organization_and_not_persisted.organization.should be_nil
216
+ end
217
+
213
218
  it "fetches belongs_to data even if it was included, only if called with parameters" do
214
219
  @user_with_included_data.organization.where(:foo_id => 1).name.should == "Bluth Company Foo"
215
220
  end
@@ -231,6 +236,7 @@ describe Her::Model::Associations do
231
236
 
232
237
  it "fetches data with the specified id when calling find" do
233
238
  comment = @user_without_included_data.comments.find(5)
239
+ comment.should be_a(Foo::Comment)
234
240
  comment.id.should eq(5)
235
241
  end
236
242
 
@@ -265,6 +271,88 @@ describe Her::Model::Associations do
265
271
  end
266
272
  end
267
273
 
274
+ context "handling associations with details in active_model_serializers format" do
275
+ before do
276
+ Her::API.setup :url => "https://api.example.com" do |builder|
277
+ builder.use Her::Middleware::FirstLevelParseJSON
278
+ builder.use Faraday::Request::UrlEncoded
279
+ builder.adapter :test do |stub|
280
+ stub.get("/users/1") { |env| [200, {}, { :user => { :id => 1, :name => "Tobias Fünke", :comments => [{ :id => 2, :body => "Tobias, you blow hard!", :user_id => 1 }, { :id => 3, :body => "I wouldn't mind kissing that man between the cheeks, so to speak", :user_id => 1 }], :role => { :id => 1, :body => "Admin" }, :organization => { :id => 1, :name => "Bluth Company" }, :organization_id => 1 } }.to_json] }
281
+ stub.get("/users/2") { |env| [200, {}, { :user => { :id => 2, :name => "Lindsay Fünke", :organization_id => 1 } }.to_json] }
282
+ stub.get("/users/1/comments") { |env| [200, {}, { :comments => [{ :id => 4, :body => "They're having a FIRESALE?" }] }.to_json] }
283
+ stub.get("/users/2/comments") { |env| [200, {}, { :comments => [{ :id => 4, :body => "They're having a FIRESALE?" }, { :id => 5, :body => "Is this the tiny town from Footloose?" }] }.to_json] }
284
+ stub.get("/users/2/comments/5") { |env| [200, {}, { :comment => { :id => 5, :body => "Is this the tiny town from Footloose?" } }.to_json] }
285
+ stub.get("/organizations/1") { |env| [200, {}, { :organization => { :id => 1, :name => "Bluth Company Foo" } }.to_json] }
286
+ end
287
+ end
288
+ spawn_model "Foo::User" do
289
+ parse_root_in_json true, :format => :active_model_serializers
290
+ has_many :comments, class_name: "Foo::Comment"
291
+ belongs_to :organization
292
+ end
293
+ spawn_model "Foo::Comment" do
294
+ belongs_to :user
295
+ parse_root_in_json true, :format => :active_model_serializers
296
+ end
297
+ spawn_model "Foo::Organization" do
298
+ parse_root_in_json true, :format => :active_model_serializers
299
+ end
300
+
301
+ @user_with_included_data = Foo::User.find(1)
302
+ @user_without_included_data = Foo::User.find(2)
303
+ end
304
+
305
+ it "maps an array of included data through has_many" do
306
+ @user_with_included_data.comments.first.should be_a(Foo::Comment)
307
+ @user_with_included_data.comments.length.should == 2
308
+ @user_with_included_data.comments.first.id.should == 2
309
+ @user_with_included_data.comments.first.body.should == "Tobias, you blow hard!"
310
+ end
311
+
312
+ it "does not refetch the parents models data if they have been fetched before" do
313
+ @user_with_included_data.comments.first.user.object_id.should == @user_with_included_data.object_id
314
+ end
315
+
316
+ it "fetches data that was not included through has_many" do
317
+ @user_without_included_data.comments.first.should be_a(Foo::Comment)
318
+ @user_without_included_data.comments.length.should == 2
319
+ @user_without_included_data.comments.first.id.should == 4
320
+ @user_without_included_data.comments.first.body.should == "They're having a FIRESALE?"
321
+ end
322
+
323
+ it "fetches has_many data even if it was included, only if called with parameters" do
324
+ @user_with_included_data.comments.where(:foo_id => 1).length.should == 1
325
+ end
326
+
327
+ it "maps an array of included data through belongs_to" do
328
+ @user_with_included_data.organization.should be_a(Foo::Organization)
329
+ @user_with_included_data.organization.id.should == 1
330
+ @user_with_included_data.organization.name.should == "Bluth Company"
331
+ end
332
+
333
+ it "fetches data that was not included through belongs_to" do
334
+ @user_without_included_data.organization.should be_a(Foo::Organization)
335
+ @user_without_included_data.organization.id.should == 1
336
+ @user_without_included_data.organization.name.should == "Bluth Company Foo"
337
+ end
338
+
339
+ it "fetches belongs_to data even if it was included, only if called with parameters" do
340
+ @user_with_included_data.organization.where(:foo_id => 1).name.should == "Bluth Company Foo"
341
+ end
342
+
343
+ it "fetches data with the specified id when calling find" do
344
+ comment = @user_without_included_data.comments.find(5)
345
+ comment.should be_a(Foo::Comment)
346
+ comment.id.should eq(5)
347
+ end
348
+
349
+ it 'includes has_many relationships in params by default' do
350
+ params = @user_with_included_data.to_params
351
+ params[:comments].should be_kind_of(Array)
352
+ params[:comments].length.should eq(2)
353
+ end
354
+ end
355
+
268
356
  context "handling associations with details" do
269
357
  before do
270
358
  Her::API.setup :url => "https://api.example.com" do |builder|
@@ -299,5 +299,91 @@ describe Her::Model::Attributes do
299
299
  user.assign_attributes(fullname: 'Tobias Fünke')
300
300
  user.fullname?.should be_truthy
301
301
  end
302
+
303
+ context "when attribute methods are already defined" do
304
+ before do
305
+ class AbstractUser
306
+ attr_accessor :fullname
307
+
308
+ def fullname?
309
+ @fullname.present?
310
+ end
311
+ end
312
+ @spawned_models << :AbstractUser
313
+
314
+ spawn_model 'Foo::User', super_class: AbstractUser do
315
+ attributes :fullname
316
+ end
317
+ end
318
+
319
+ it "overrides getter method" do
320
+ Foo::User.generated_attribute_methods.instance_methods.should include(:fullname)
321
+ end
322
+
323
+ it "overrides setter method" do
324
+ Foo::User.generated_attribute_methods.instance_methods.should include(:fullname=)
325
+ end
326
+
327
+ it "overrides predicate method" do
328
+ Foo::User.generated_attribute_methods.instance_methods.should include(:fullname?)
329
+ end
330
+
331
+ it "defines setter that affects @attributes" do
332
+ user = Foo::User.new
333
+ user.fullname = 'Tobias Fünke'
334
+ user.attributes[:fullname].should eq('Tobias Fünke')
335
+ end
336
+
337
+ it "defines getter that reads @attributes" do
338
+ user = Foo::User.new
339
+ user.attributes[:fullname] = 'Tobias Fünke'
340
+ user.fullname.should eq('Tobias Fünke')
341
+ end
342
+
343
+ it "defines predicate that reads @attributes" do
344
+ user = Foo::User.new
345
+ user.fullname?.should be_falsey
346
+ user.attributes[:fullname] = 'Tobias Fünke'
347
+ user.fullname?.should be_truthy
348
+ end
349
+ end
350
+
351
+ if ActiveModel::VERSION::MAJOR < 4
352
+ it "creates a new mutex" do
353
+ expect(Mutex).to receive(:new).once.and_call_original
354
+ spawn_model 'Foo::User' do
355
+ attributes :fullname
356
+ end
357
+ Foo::User.attribute_methods_mutex.should_not eq(Foo::User.generated_attribute_methods)
358
+ end
359
+
360
+ it "works well with Module#synchronize monkey patched by ActiveSupport" do
361
+ Module.class_eval do
362
+ def synchronize(*args)
363
+ raise 'gotcha!'
364
+ end
365
+ end
366
+ expect(Mutex).to receive(:new).once.and_call_original
367
+ spawn_model 'Foo::User' do
368
+ attributes :fullname
369
+ end
370
+ Foo::User.attribute_methods_mutex.should_not eq(Foo::User.generated_attribute_methods)
371
+ Module.class_eval do
372
+ undef :synchronize
373
+ end
374
+ end
375
+ else
376
+ it "uses ActiveModel's mutex" do
377
+ Foo::User.attribute_methods_mutex.should eq(Foo::User.generated_attribute_methods)
378
+ end
379
+ end
380
+
381
+ it "uses a mutex" do
382
+ spawn_model 'Foo::User'
383
+ expect(Foo::User.attribute_methods_mutex).to receive(:synchronize).once.and_call_original
384
+ Foo::User.class_eval do
385
+ attributes :fullname, :documents
386
+ end
387
+ end
302
388
  end
303
389
  end
@@ -12,42 +12,6 @@ describe Her::Model::HTTP do
12
12
  Her::API.setup :url => "https://api.example.com"
13
13
  end
14
14
 
15
- context "when binding a model to an instance of Her::API" do
16
- before { Foo::User.uses_api api1 }
17
- subject { Foo::User.her_api }
18
- its(:base_uri) { should == "https://api1.example.com" }
19
- end
20
-
21
- context "when binding a model directly to Her::API" do
22
- before { spawn_model "Foo::User" }
23
- subject { Foo::User.her_api }
24
- its(:base_uri) { should == "https://api.example.com" }
25
- end
26
-
27
- context "when using a proc for uses_api" do
28
- before do
29
- Foo::User.uses_api lambda { Her::API.new :url => 'http://api-lambda.example.com' }
30
- end
31
-
32
- specify { Foo::User.her_api.base_uri.should == 'http://api-lambda.example.com' }
33
- end
34
-
35
- context "when binding two models to two different instances of Her::API" do
36
- before do
37
- Foo::User.uses_api api1
38
- Foo::Comment.uses_api api2
39
- end
40
-
41
- specify { Foo::User.her_api.base_uri.should == "https://api1.example.com" }
42
- specify { Foo::Comment.her_api.base_uri.should == "https://api2.example.com" }
43
- end
44
-
45
- context "binding one model to Her::API and another one to an instance of Her::API" do
46
- before { Foo::Comment.uses_api api2 }
47
- specify { Foo::User.her_api.base_uri.should == "https://api.example.com" }
48
- specify { Foo::Comment.her_api.base_uri.should == "https://api2.example.com" }
49
- end
50
-
51
15
  context "when binding a model to its superclass' her_api" do
52
16
  before do
53
17
  spawn_model "Foo::Superclass"
@@ -49,10 +49,12 @@ describe Her::Model::ORM do
49
49
  it "handles new resource" do
50
50
  @new_user = Foo::User.new(:fullname => "Tobias Fünke")
51
51
  @new_user.new?.should be_truthy
52
+ @new_user.new_record?.should be_truthy
52
53
  @new_user.fullname.should == "Tobias Fünke"
53
54
 
54
55
  @existing_user = Foo::User.find(1)
55
56
  @existing_user.new?.should be_falsey
57
+ @existing_user.new_record?.should be_falsey
56
58
  end
57
59
 
58
60
  it 'handles new resource with custom primary key' do
@@ -8,60 +8,66 @@ describe Her::Model::Paths do
8
8
  spawn_model "Foo::User"
9
9
  end
10
10
 
11
- describe "#build_request_path" do
11
+ describe "#request_path" do
12
12
  it "builds paths with defaults" do
13
- Foo::User.build_request_path(:id => "foo").should == "users/foo"
14
- Foo::User.build_request_path(:id => nil).should == "users"
15
- Foo::User.build_request_path.should == "users"
13
+ Foo::User.new(:id => "foo").request_path.should == "users/foo"
14
+ Foo::User.new(:id => nil).request_path.should == "users"
15
+ Foo::User.new().request_path.should == "users"
16
16
  end
17
17
 
18
18
  it "builds paths with custom collection path" do
19
19
  Foo::User.collection_path "/utilisateurs"
20
- Foo::User.build_request_path(:id => "foo").should == "/utilisateurs/foo"
21
- Foo::User.build_request_path.should == "/utilisateurs"
20
+ Foo::User.new(:id => "foo").request_path.should == "/utilisateurs/foo"
21
+ Foo::User.new().request_path.should == "/utilisateurs"
22
22
  end
23
23
 
24
24
  it "builds paths with custom relative collection path" do
25
25
  Foo::User.collection_path "utilisateurs"
26
- Foo::User.build_request_path(:id => "foo").should == "utilisateurs/foo"
27
- Foo::User.build_request_path.should == "utilisateurs"
26
+ Foo::User.new(:id => "foo").request_path.should == "utilisateurs/foo"
27
+ Foo::User.new().request_path.should == "utilisateurs"
28
28
  end
29
29
 
30
30
  it "builds paths with custom collection path with multiple variables" do
31
31
  Foo::User.collection_path "/organizations/:organization_id/utilisateurs"
32
32
 
33
- Foo::User.build_request_path(:id => "foo", :_organization_id => "acme").should == "/organizations/acme/utilisateurs/foo"
34
- Foo::User.build_request_path(:_organization_id => "acme").should == "/organizations/acme/utilisateurs"
33
+ Foo::User.new(:id => "foo").request_path(:_organization_id => "acme").should == "/organizations/acme/utilisateurs/foo"
34
+ Foo::User.new().request_path(:_organization_id => "acme").should == "/organizations/acme/utilisateurs"
35
35
 
36
- Foo::User.build_request_path(:id => "foo", :organization_id => "acme").should == "/organizations/acme/utilisateurs/foo"
37
- Foo::User.build_request_path(:organization_id => "acme").should == "/organizations/acme/utilisateurs"
36
+ Foo::User.new(:id => "foo", :organization_id => "acme").request_path.should == "/organizations/acme/utilisateurs/foo"
37
+ Foo::User.new(:organization_id => "acme").request_path.should == "/organizations/acme/utilisateurs"
38
38
  end
39
39
 
40
40
  it "builds paths with custom relative collection path with multiple variables" do
41
41
  Foo::User.collection_path "organizations/:organization_id/utilisateurs"
42
42
 
43
- Foo::User.build_request_path(:id => "foo", :_organization_id => "acme").should == "organizations/acme/utilisateurs/foo"
44
- Foo::User.build_request_path(:_organization_id => "acme").should == "organizations/acme/utilisateurs"
43
+ Foo::User.new(:id => "foo").request_path(:_organization_id => "acme").should == "organizations/acme/utilisateurs/foo"
44
+ Foo::User.new().request_path(:_organization_id => "acme").should == "organizations/acme/utilisateurs"
45
45
 
46
- Foo::User.build_request_path(:id => "foo", :organization_id => "acme").should == "organizations/acme/utilisateurs/foo"
47
- Foo::User.build_request_path(:organization_id => "acme").should == "organizations/acme/utilisateurs"
46
+ Foo::User.new(:id => "foo", :organization_id => "acme").request_path.should == "organizations/acme/utilisateurs/foo"
47
+ Foo::User.new(:organization_id => "acme").request_path.should == "organizations/acme/utilisateurs"
48
48
  end
49
49
 
50
50
  it "builds paths with custom item path" do
51
51
  Foo::User.resource_path "/utilisateurs/:id"
52
- Foo::User.build_request_path(:id => "foo").should == "/utilisateurs/foo"
53
- Foo::User.build_request_path.should == "users"
52
+ Foo::User.new(:id => "foo").request_path.should == "/utilisateurs/foo"
53
+ Foo::User.new().request_path.should == "users"
54
54
  end
55
55
 
56
56
  it "builds paths with custom relative item path" do
57
57
  Foo::User.resource_path "utilisateurs/:id"
58
- Foo::User.build_request_path(:id => "foo").should == "utilisateurs/foo"
59
- Foo::User.build_request_path.should == "users"
58
+ Foo::User.new(:id => "foo").request_path.should == "utilisateurs/foo"
59
+ Foo::User.new().request_path.should == "users"
60
60
  end
61
61
 
62
62
  it "raises exceptions when building a path without required custom variables" do
63
63
  Foo::User.collection_path "/organizations/:organization_id/utilisateurs"
64
- expect { Foo::User.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/utilisateurs/:id`. Parameters are `{:id=>\"foo\"}`.")
64
+ expect { Foo::User.new(:id => "foo").request_path }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/utilisateurs/:id`. Parameters are `{:id=>\"foo\"}`.")
65
+ end
66
+
67
+ it "escapes the variable values" do
68
+ Foo::User.collection_path "organizations/:organization_id/utilisateurs"
69
+ Foo::User.new(:id => "Привет").request_path(:_organization_id => 'лол').should == "organizations/%D0%BB%D0%BE%D0%BB/utilisateurs/%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82"
70
+ Foo::User.new(:organization_id => 'лол', :id => "Привет").request_path.should == "organizations/%D0%BB%D0%BE%D0%BB/utilisateurs/%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82"
65
71
  end
66
72
  end
67
73
  end
@@ -71,56 +77,56 @@ describe Her::Model::Paths do
71
77
  spawn_model "Foo::AdminUser"
72
78
  end
73
79
 
74
- describe "#build_request_path" do
80
+ describe "#request_path" do
75
81
  it "builds paths with defaults" do
76
- Foo::AdminUser.build_request_path(:id => "foo").should == "admin_users/foo"
77
- Foo::AdminUser.build_request_path.should == "admin_users"
82
+ Foo::AdminUser.new(:id => "foo").request_path.should == "admin_users/foo"
83
+ Foo::AdminUser.new().request_path.should == "admin_users"
78
84
  end
79
85
 
80
86
  it "builds paths with custom collection path" do
81
87
  Foo::AdminUser.collection_path "/users"
82
- Foo::AdminUser.build_request_path(:id => "foo").should == "/users/foo"
83
- Foo::AdminUser.build_request_path.should == "/users"
88
+ Foo::AdminUser.new(:id => "foo").request_path.should == "/users/foo"
89
+ Foo::AdminUser.new().request_path.should == "/users"
84
90
  end
85
91
 
86
92
  it "builds paths with custom relative collection path" do
87
93
  Foo::AdminUser.collection_path "users"
88
- Foo::AdminUser.build_request_path(:id => "foo").should == "users/foo"
89
- Foo::AdminUser.build_request_path.should == "users"
94
+ Foo::AdminUser.new(:id => "foo").request_path.should == "users/foo"
95
+ Foo::AdminUser.new().request_path.should == "users"
90
96
  end
91
97
 
92
98
  it "builds paths with custom collection path with multiple variables" do
93
99
  Foo::AdminUser.collection_path "/organizations/:organization_id/users"
94
- Foo::AdminUser.build_request_path(:id => "foo", :_organization_id => "acme").should == "/organizations/acme/users/foo"
95
- Foo::AdminUser.build_request_path(:_organization_id => "acme").should == "/organizations/acme/users"
100
+ Foo::AdminUser.new(:id => "foo").request_path(:_organization_id => "acme").should == "/organizations/acme/users/foo"
101
+ Foo::AdminUser.new().request_path(:_organization_id => "acme").should == "/organizations/acme/users"
96
102
  end
97
103
 
98
104
  it "builds paths with custom relative collection path with multiple variables" do
99
105
  Foo::AdminUser.collection_path "organizations/:organization_id/users"
100
- Foo::AdminUser.build_request_path(:id => "foo", :_organization_id => "acme").should == "organizations/acme/users/foo"
101
- Foo::AdminUser.build_request_path(:_organization_id => "acme").should == "organizations/acme/users"
106
+ Foo::AdminUser.new(:id => "foo").request_path(:_organization_id => "acme").should == "organizations/acme/users/foo"
107
+ Foo::AdminUser.new().request_path(:_organization_id => "acme").should == "organizations/acme/users"
102
108
  end
103
109
 
104
110
  it "builds paths with custom item path" do
105
111
  Foo::AdminUser.resource_path "/users/:id"
106
- Foo::AdminUser.build_request_path(:id => "foo").should == "/users/foo"
107
- Foo::AdminUser.build_request_path.should == "admin_users"
112
+ Foo::AdminUser.new(:id => "foo").request_path.should == "/users/foo"
113
+ Foo::AdminUser.new().request_path.should == "admin_users"
108
114
  end
109
115
 
110
116
  it "builds paths with custom relative item path" do
111
117
  Foo::AdminUser.resource_path "users/:id"
112
- Foo::AdminUser.build_request_path(:id => "foo").should == "users/foo"
113
- Foo::AdminUser.build_request_path.should == "admin_users"
118
+ Foo::AdminUser.new(:id => "foo").request_path.should == "users/foo"
119
+ Foo::AdminUser.new().request_path.should == "admin_users"
114
120
  end
115
121
 
116
122
  it "raises exceptions when building a path without required custom variables" do
117
123
  Foo::AdminUser.collection_path "/organizations/:organization_id/users"
118
- expect { Foo::AdminUser.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.")
124
+ expect { Foo::AdminUser.new(:id => "foo").request_path }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `/organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.")
119
125
  end
120
126
 
121
127
  it "raises exceptions when building a relative path without required custom variables" do
122
128
  Foo::AdminUser.collection_path "organizations/:organization_id/users"
123
- expect { Foo::AdminUser.build_request_path(:id => "foo") }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.")
129
+ expect { Foo::AdminUser.new(:id => "foo").request_path }.to raise_error(Her::Errors::PathError, "Missing :_organization_id parameter to build the request path. Path is `organizations/:organization_id/users/:id`. Parameters are `{:id=>\"foo\"}`.")
124
130
  end
125
131
  end
126
132
  end
@@ -152,10 +158,10 @@ describe Her::Model::Paths do
152
158
  spawn_model "Foo::User"
153
159
  end
154
160
 
155
- describe "#build_request_path" do
161
+ describe "#request_path" do
156
162
  it "builds paths with defaults" do
157
- Foo::User.build_request_path(:id => "foo").should == "users/foo"
158
- Foo::User.build_request_path.should == "users"
163
+ Foo::User.new(:id => "foo").request_path.should == "users/foo"
164
+ Foo::User.new.request_path.should == "users"
159
165
  end
160
166
  end
161
167
  end
@@ -173,15 +179,15 @@ describe Her::Model::Paths do
173
179
  end
174
180
  end
175
181
 
176
- describe '#build_request_path' do
182
+ describe '#request_path' do
177
183
  it 'uses the correct primary key attribute' do
178
- User.build_request_path(:UserId => 'foo').should == 'users/foo'
179
- User.build_request_path(:id => 'foo').should == 'users'
184
+ User.new(:UserId => 'foo').request_path.should == 'users/foo'
185
+ User.new(:id => 'foo').request_path.should == 'users'
180
186
  end
181
187
 
182
188
  it 'replaces :id with the appropriate primary key' do
183
- Customer.build_request_path(:customer_id => 'joe').should == 'customers/joe'
184
- Customer.build_request_path(:id => 'joe').should == 'customers'
189
+ Customer.new(:customer_id => 'joe').request_path.should == 'customers/joe'
190
+ Customer.new(:id => 'joe').request_path.should == 'customers'
185
191
  end
186
192
  end
187
193
  end
@@ -3,21 +3,28 @@ module Her
3
3
  module Macros
4
4
  module ModelMacros
5
5
  # Create a class and automatically inject Her::Model into it
6
- def spawn_model(klass, &block)
6
+ def spawn_model(klass, options={}, &block)
7
+ super_class = options[:super_class]
8
+ model_type = options[:type] || Her::Model
9
+ new_class = if super_class
10
+ Class.new(super_class)
11
+ else
12
+ Class.new
13
+ end
7
14
  if klass =~ /::/
8
15
  base, submodel = klass.split(/::/).map{ |s| s.to_sym }
9
16
  Object.const_set(base, Module.new) unless Object.const_defined?(base)
10
17
  Object.const_get(base).module_eval do
11
18
  remove_const submodel if constants.map(&:to_sym).include?(submodel)
12
- submodel = const_set(submodel, Class.new)
13
- submodel.send(:include, Her::Model)
19
+ submodel = const_set(submodel, new_class)
20
+ submodel.send(:include, model_type)
14
21
  submodel.class_eval(&block) if block_given?
15
22
  end
16
23
 
17
24
  @spawned_models << base
18
25
  else
19
26
  Object.instance_eval { remove_const klass } if Object.const_defined?(klass)
20
- Object.const_set(klass, Class.new).send(:include, Her::Model)
27
+ Object.const_set(klass, Class.new).send(:include, model_type)
21
28
  Object.const_get(klass).class_eval(&block) if block_given?
22
29
 
23
30
  @spawned_models << klass.to_sym
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.7.6
4
+ version: 0.8.1
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: 2015-06-16 00:00:00.000000000 Z
11
+ date: 2015-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -180,9 +180,11 @@ files:
180
180
  - lib/her/api.rb
181
181
  - lib/her/collection.rb
182
182
  - lib/her/errors.rb
183
+ - lib/her/json_api/model.rb
183
184
  - lib/her/middleware.rb
184
185
  - lib/her/middleware/accept_json.rb
185
186
  - lib/her/middleware/first_level_parse_json.rb
187
+ - lib/her/middleware/json_api_parser.rb
186
188
  - lib/her/middleware/parse_json.rb
187
189
  - lib/her/middleware/second_level_parse_json.rb
188
190
  - lib/her/model.rb
@@ -205,8 +207,10 @@ files:
205
207
  - lib/her/version.rb
206
208
  - spec/api_spec.rb
207
209
  - spec/collection_spec.rb
210
+ - spec/json_api/model_spec.rb
208
211
  - spec/middleware/accept_json_spec.rb
209
212
  - spec/middleware/first_level_parse_json_spec.rb
213
+ - spec/middleware/json_api_parser_spec.rb
210
214
  - spec/middleware/second_level_parse_json_spec.rb
211
215
  - spec/model/associations/association_proxy_spec.rb
212
216
  - spec/model/associations_spec.rb
@@ -256,8 +260,10 @@ summary: A simple Representational State Transfer-based Hypertext Transfer Proto
256
260
  test_files:
257
261
  - spec/api_spec.rb
258
262
  - spec/collection_spec.rb
263
+ - spec/json_api/model_spec.rb
259
264
  - spec/middleware/accept_json_spec.rb
260
265
  - spec/middleware/first_level_parse_json_spec.rb
266
+ - spec/middleware/json_api_parser_spec.rb
261
267
  - spec/middleware/second_level_parse_json_spec.rb
262
268
  - spec/model/associations/association_proxy_spec.rb
263
269
  - spec/model/associations_spec.rb