her 0.8.2 → 0.10.4
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 +4 -4
 - data/.rspec +1 -1
 - data/.rubocop.yml +1291 -0
 - data/.travis.yml +6 -1
 - data/README.md +29 -11
 - data/her.gemspec +3 -5
 - data/lib/her/api.rb +16 -9
 - data/lib/her/middleware/json_api_parser.rb +1 -1
 - data/lib/her/model/associations/association.rb +32 -5
 - data/lib/her/model/associations/association_proxy.rb +1 -1
 - data/lib/her/model/associations/belongs_to_association.rb +1 -1
 - data/lib/her/model/associations/has_many_association.rb +3 -3
 - data/lib/her/model/attributes.rb +105 -75
 - data/lib/her/model/http.rb +3 -3
 - data/lib/her/model/introspection.rb +1 -1
 - data/lib/her/model/orm.rb +96 -19
 - data/lib/her/model/parse.rb +27 -17
 - data/lib/her/model/relation.rb +46 -2
 - data/lib/her/version.rb +1 -1
 - data/spec/api_spec.rb +34 -31
 - data/spec/collection_spec.rb +25 -10
 - data/spec/json_api/model_spec.rb +75 -72
 - data/spec/middleware/accept_json_spec.rb +1 -1
 - data/spec/middleware/first_level_parse_json_spec.rb +20 -20
 - data/spec/middleware/json_api_parser_spec.rb +26 -7
 - data/spec/middleware/second_level_parse_json_spec.rb +8 -9
 - data/spec/model/associations/association_proxy_spec.rb +2 -5
 - data/spec/model/associations_spec.rb +617 -295
 - data/spec/model/attributes_spec.rb +114 -107
 - data/spec/model/callbacks_spec.rb +59 -27
 - data/spec/model/dirty_spec.rb +70 -29
 - data/spec/model/http_spec.rb +67 -35
 - data/spec/model/introspection_spec.rb +26 -22
 - data/spec/model/nested_attributes_spec.rb +31 -31
 - data/spec/model/orm_spec.rb +332 -157
 - data/spec/model/parse_spec.rb +250 -77
 - data/spec/model/paths_spec.rb +109 -109
 - data/spec/model/relation_spec.rb +89 -69
 - data/spec/model/validations_spec.rb +6 -6
 - data/spec/model_spec.rb +17 -17
 - data/spec/spec_helper.rb +2 -3
 - data/spec/support/macros/model_macros.rb +2 -2
 - metadata +36 -63
 
    
        data/.travis.yml
    CHANGED
    
    | 
         @@ -3,7 +3,9 @@ language: ruby 
     | 
|
| 
       3 
3 
     | 
    
         
             
            sudo: false
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            rvm:
         
     | 
| 
       6 
     | 
    
         
            -
              - 2. 
     | 
| 
      
 6 
     | 
    
         
            +
              - 2.4.2
         
     | 
| 
      
 7 
     | 
    
         
            +
              - 2.3.5
         
     | 
| 
      
 8 
     | 
    
         
            +
              - 2.2.8
         
     | 
| 
       7 
9 
     | 
    
         
             
              - 2.1.6
         
     | 
| 
       8 
10 
     | 
    
         
             
              - 2.0.0
         
     | 
| 
       9 
11 
     | 
    
         
             
              - 1.9.3
         
     | 
| 
         @@ -14,4 +16,7 @@ gemfile: 
     | 
|
| 
       14 
16 
     | 
    
         
             
              - gemfiles/Gemfile.activemodel-4.0
         
     | 
| 
       15 
17 
     | 
    
         
             
              - gemfiles/Gemfile.activemodel-3.2.x
         
     | 
| 
       16 
18 
     | 
    
         | 
| 
      
 19 
     | 
    
         
            +
            before_install:
         
     | 
| 
      
 20 
     | 
    
         
            +
              - gem install bundler
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
       17 
22 
     | 
    
         
             
            script: "echo 'COME ON!' && bundle exec rake spec"
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,8 +1,3 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # Maintenance Update 29th Sept 2016
         
     | 
| 
       2 
     | 
    
         
            -
            Hi folks, [@edtjones](https://github.com/edtjones) here. Rémi has handed me the keys to Her and [@foxpaul](https://github.com/foxpaul) and I will be trying to do the library justice with the help of the community. There's loads to do; we'll get in touch with everyone who's raised a PR as soon as possible and figure out a plan of action.
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
            # Rails 5 support
         
     | 
| 
       5 
     | 
    
         
            -
            If you need Rails 5 support, version 0.8.2 is for you!
         
     | 
| 
       6 
1 
     | 
    
         | 
| 
       7 
2 
     | 
    
         
             
            <p align="center">
         
     | 
| 
       8 
3 
     | 
    
         
             
              <a href="https://github.com/remiprev/her">
         
     | 
| 
         @@ -15,6 +10,7 @@ If you need Rails 5 support, version 0.8.2 is for you! 
     | 
|
| 
       15 
10 
     | 
    
         
             
              <a href="https://codeclimate.com/github/remiprev/her"><img src="http://img.shields.io/codeclimate/github/remiprev/her.svg" /></a>
         
     | 
| 
       16 
11 
     | 
    
         
             
              <a href='https://gemnasium.com/remiprev/her'><img src="http://img.shields.io/gemnasium/remiprev/her.svg" /></a>
         
     | 
| 
       17 
12 
     | 
    
         
             
              <a href="https://travis-ci.org/remiprev/her"><img src="http://img.shields.io/travis/remiprev/her/master.svg" /></a>
         
     | 
| 
      
 13 
     | 
    
         
            +
              <a href="https://gitter.im/her-orm/Lobby"><img src="https://badges.gitter.im/her-orm/Lobby.png" alt="Gitter chat" title="" data-pin-nopin="true"></a>
         
     | 
| 
       18 
14 
     | 
    
         
             
            </p>
         
     | 
| 
       19 
15 
     | 
    
         | 
| 
       20 
16 
     | 
    
         
             
            ---
         
     | 
| 
         @@ -173,6 +169,24 @@ end 
     | 
|
| 
       173 
169 
     | 
    
         | 
| 
       174 
170 
     | 
    
         
             
            Now, each HTTP request made by Her will have the `X-API-Token` header.
         
     | 
| 
       175 
171 
     | 
    
         | 
| 
      
 172 
     | 
    
         
            +
            ### Basic Http Authentication
         
     | 
| 
      
 173 
     | 
    
         
            +
            Her can use basic http auth by adding a line to your initializer
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 176 
     | 
    
         
            +
            # config/initializers/her.rb
         
     | 
| 
      
 177 
     | 
    
         
            +
            Her::API.setup url: "https://api.example.com" do |c|
         
     | 
| 
      
 178 
     | 
    
         
            +
              # Request
         
     | 
| 
      
 179 
     | 
    
         
            +
              c.use Faraday::Request::BasicAuthentication, 'myusername', 'mypassword'
         
     | 
| 
      
 180 
     | 
    
         
            +
              c.use Faraday::Request::UrlEncoded
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
              # Response
         
     | 
| 
      
 183 
     | 
    
         
            +
              c.use Her::Middleware::DefaultParseJSON
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
              # Adapter
         
     | 
| 
      
 186 
     | 
    
         
            +
              c.use Faraday::Adapter::NetHttp
         
     | 
| 
      
 187 
     | 
    
         
            +
            end
         
     | 
| 
      
 188 
     | 
    
         
            +
            ```
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
       176 
190 
     | 
    
         
             
            ### OAuth
         
     | 
| 
       177 
191 
     | 
    
         | 
| 
       178 
192 
     | 
    
         
             
            Using the `faraday_middleware` and `simple_oauth` gems, it’s fairly easy to use OAuth authentication with Her.
         
     | 
| 
         @@ -214,7 +228,7 @@ end 
     | 
|
| 
       214 
228 
     | 
    
         
             
            @tweets = Tweet.get("/statuses/home_timeline.json")
         
     | 
| 
       215 
229 
     | 
    
         
             
            ```
         
     | 
| 
       216 
230 
     | 
    
         | 
| 
       217 
     | 
    
         
            -
            See the *Authentication 
     | 
| 
      
 231 
     | 
    
         
            +
            See the [*Authentication middleware section*](#authentication) for an example of how to pass different credentials based on the current user.
         
     | 
| 
       218 
232 
     | 
    
         | 
| 
       219 
233 
     | 
    
         
             
            ### Parsing JSON data
         
     | 
| 
       220 
234 
     | 
    
         | 
| 
         @@ -228,7 +242,7 @@ By default, Her handles JSON data. It expects the resource/collection data to be 
     | 
|
| 
       228 
242 
     | 
    
         
             
            [{ "id" : 1, "name" : "Tobias Fünke" }]
         
     | 
| 
       229 
243 
     | 
    
         
             
            ```
         
     | 
| 
       230 
244 
     | 
    
         | 
| 
       231 
     | 
    
         
            -
            However, if you want Her to be able to parse the data from a single root element (usually based on the model name), you’ll have to use the `parse_root_in_json` method (See the **JSON attributes-wrapping** section).
         
     | 
| 
      
 245 
     | 
    
         
            +
            However, if you want Her to be able to parse the data from a single root element (usually based on the model name), you’ll have to use the `parse_root_in_json` method (See the [**JSON attributes-wrapping**](#json-attributes-wrapping) section).
         
     | 
| 
       232 
246 
     | 
    
         | 
| 
       233 
247 
     | 
    
         
             
            Also, you can define your own parsing method using a response middleware. The middleware should set `env[:body]` to a hash with three symbol keys: `:data`, `:errors` and `:metadata`. The following code uses a custom middleware to parse the JSON data:
         
     | 
| 
       234 
248 
     | 
    
         | 
| 
         @@ -397,8 +411,8 @@ You can use the association methods to build new objects and save them. 
     | 
|
| 
       397 
411 
     | 
    
         
             
            @user.comments.build(body: "Just a draft")
         
     | 
| 
       398 
412 
     | 
    
         
             
            # => [#<Comment body="Just a draft" user_id=1>]
         
     | 
| 
       399 
413 
     | 
    
         | 
| 
       400 
     | 
    
         
            -
            @user.comments.create(body: "Hello world.")
         
     | 
| 
       401 
     | 
    
         
            -
            # POST "/ 
     | 
| 
      
 414 
     | 
    
         
            +
            @user.comments.create(body: "Hello world.", user_id: 1)
         
     | 
| 
      
 415 
     | 
    
         
            +
            # POST "/comments" with `body=Hello+world.&user_id=1`
         
     | 
| 
       402 
416 
     | 
    
         
             
            # => [#<Comment id=3 body="Hello world." user_id=1>]
         
     | 
| 
       403 
417 
     | 
    
         
             
            ```
         
     | 
| 
       404 
418 
     | 
    
         | 
| 
         @@ -432,7 +446,7 @@ class Organization 
     | 
|
| 
       432 
446 
     | 
    
         
             
            end
         
     | 
| 
       433 
447 
     | 
    
         
             
            ```
         
     | 
| 
       434 
448 
     | 
    
         | 
| 
       435 
     | 
    
         
            -
            Her expects all `User` resources to have an `:organization_id` (or `:_organization_id`) attribute. Otherwise, calling mostly all methods, like `User.all`, will  
     | 
| 
      
 449 
     | 
    
         
            +
            Her expects all `User` resources to have an `:organization_id` (or `:_organization_id`) attribute. Otherwise, calling mostly all methods, like `User.all`, will throw an exception like this one:
         
     | 
| 
       436 
450 
     | 
    
         | 
| 
       437 
451 
     | 
    
         
             
            ```ruby
         
     | 
| 
       438 
452 
     | 
    
         
             
            Her::Errors::PathError: Missing :_organization_id parameter to build the request path. Path is `organizations/:organization_id/users`. Parameters are `{ … }`.
         
     | 
| 
         @@ -593,7 +607,7 @@ users = Users.all 
     | 
|
| 
       593 
607 
     | 
    
         
             
            #### JSON API support
         
     | 
| 
       594 
608 
     | 
    
         | 
| 
       595 
609 
     | 
    
         
             
            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
         
     | 
| 
       596 
     | 
    
         
            -
            of the data is as follows: 
     | 
| 
      
 610 
     | 
    
         
            +
            of the data is as follows:
         
     | 
| 
       597 
611 
     | 
    
         | 
| 
       598 
612 
     | 
    
         
             
            ```json
         
     | 
| 
       599 
613 
     | 
    
         
             
            { "data": {
         
     | 
| 
         @@ -976,6 +990,7 @@ See the [UPGRADE.md](https://github.com/remiprev/her/blob/master/UPGRADE.md) for 
     | 
|
| 
       976 
990 
     | 
    
         
             
            Most projects I know that use Her are internal or private projects but here’s a list of public ones:
         
     | 
| 
       977 
991 
     | 
    
         | 
| 
       978 
992 
     | 
    
         
             
            * [tumbz](https://github.com/remiprev/tumbz)
         
     | 
| 
      
 993 
     | 
    
         
            +
            * [zoho-ruby](https://github.com/errorstudio/zoho-ruby)
         
     | 
| 
       979 
994 
     | 
    
         
             
            * [crowdher](https://github.com/simonprev/crowdher)
         
     | 
| 
       980 
995 
     | 
    
         
             
            * [vodka](https://github.com/magnolia-fan/vodka)
         
     | 
| 
       981 
996 
     | 
    
         
             
            * [webistrano_cli](https://github.com/chytreg/webistrano_cli)
         
     | 
| 
         @@ -987,6 +1002,9 @@ I told myself a few months ago that it would be great to build a gem to replace 
     | 
|
| 
       987 
1002 
     | 
    
         | 
| 
       988 
1003 
     | 
    
         
             
            Most of Her’s core concepts were written on a Saturday morning of April 2012 ([first commit](https://github.com/remiprev/her/commit/689d8e88916dc2ad258e69a2a91a283f061cbef2) at 7am!).
         
     | 
| 
       989 
1004 
     | 
    
         | 
| 
      
 1005 
     | 
    
         
            +
            ## Maintainers
         
     | 
| 
      
 1006 
     | 
    
         
            +
            The gem is currently maintained by [@zacharywelch](https://github.com/zacharywelch) and [@edtjones](https://github.com/edtjones).
         
     | 
| 
      
 1007 
     | 
    
         
            +
             
     | 
| 
       990 
1008 
     | 
    
         
             
            ## Contribute
         
     | 
| 
       991 
1009 
     | 
    
         | 
| 
       992 
1010 
     | 
    
         
             
            Yes please! Feel free to contribute and submit issues/pull requests [on GitHub](https://github.com/remiprev/her/issues). There’s no such thing as a bad pull request — even if it’s for a typo, a small improvement to the code or the documentation!
         
     | 
    
        data/her.gemspec
    CHANGED
    
    | 
         @@ -18,13 +18,11 @@ Gem::Specification.new do |s| 
     | 
|
| 
       18 
18 
     | 
    
         
             
              s.require_paths = ["lib"]
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
              s.add_development_dependency "rake", "~> 10.0"
         
     | 
| 
       21 
     | 
    
         
            -
              s.add_development_dependency "rspec", "~>  
     | 
| 
       22 
     | 
    
         
            -
              s.add_development_dependency "rspec-its", "~> 1.0"
         
     | 
| 
       23 
     | 
    
         
            -
              s.add_development_dependency "fivemat", "~> 1.2"
         
     | 
| 
      
 21 
     | 
    
         
            +
              s.add_development_dependency "rspec", "~> 3.5"
         
     | 
| 
       24 
22 
     | 
    
         
             
              s.add_development_dependency "json", "~> 1.8"
         
     | 
| 
       25 
23 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
              s.add_runtime_dependency "activemodel", ">= 3.0.0", " 
     | 
| 
       27 
     | 
    
         
            -
              s.add_runtime_dependency "activesupport", ">= 3.0.0", " 
     | 
| 
      
 24 
     | 
    
         
            +
              s.add_runtime_dependency "activemodel", ">= 3.0.0", "< 5.2.0"
         
     | 
| 
      
 25 
     | 
    
         
            +
              s.add_runtime_dependency "activesupport", ">= 3.0.0", "< 5.2.0"
         
     | 
| 
       28 
26 
     | 
    
         
             
              s.add_runtime_dependency "faraday", ">= 0.8", "< 1.0"
         
     | 
| 
       29 
27 
     | 
    
         
             
              s.add_runtime_dependency "multi_json", "~> 1.7"
         
     | 
| 
       30 
28 
     | 
    
         
             
            end
         
     | 
    
        data/lib/her/api.rb
    CHANGED
    
    | 
         @@ -89,19 +89,26 @@ module Her 
     | 
|
| 
       89 
89 
     | 
    
         
             
                  path = opts.delete(:_path)
         
     | 
| 
       90 
90 
     | 
    
         
             
                  headers = opts.delete(:_headers)
         
     | 
| 
       91 
91 
     | 
    
         
             
                  opts.delete_if { |key, value| key.to_s =~ /^_/ } # Remove all internal parameters
         
     | 
| 
       92 
     | 
    
         
            -
                   
     | 
| 
      
 92 
     | 
    
         
            +
                  if method == :options
         
     | 
| 
      
 93 
     | 
    
         
            +
                    # Faraday doesn't support the OPTIONS verb because of a name collision with an internal options method
         
     | 
| 
      
 94 
     | 
    
         
            +
                    # so we need to call run_request directly.
         
     | 
| 
       93 
95 
     | 
    
         
             
                    request.headers.merge!(headers) if headers
         
     | 
| 
       94 
     | 
    
         
            -
                     
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
                       
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
      
 96 
     | 
    
         
            +
                    response = @connection.run_request method, path, opts, headers
         
     | 
| 
      
 97 
     | 
    
         
            +
                  else
         
     | 
| 
      
 98 
     | 
    
         
            +
                    response = @connection.send method do |request|
         
     | 
| 
      
 99 
     | 
    
         
            +
                      request.headers.merge!(headers) if headers
         
     | 
| 
      
 100 
     | 
    
         
            +
                      if method == :get
         
     | 
| 
      
 101 
     | 
    
         
            +
                        # For GET requests, treat additional parameters as querystring data
         
     | 
| 
      
 102 
     | 
    
         
            +
                        request.url path, opts
         
     | 
| 
      
 103 
     | 
    
         
            +
                      else
         
     | 
| 
      
 104 
     | 
    
         
            +
                        # For POST, PUT and DELETE requests, treat additional parameters as request body
         
     | 
| 
      
 105 
     | 
    
         
            +
                        request.url path
         
     | 
| 
      
 106 
     | 
    
         
            +
                        request.body = opts
         
     | 
| 
      
 107 
     | 
    
         
            +
                      end
         
     | 
| 
       101 
108 
     | 
    
         
             
                    end
         
     | 
| 
       102 
109 
     | 
    
         
             
                  end
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
110 
     | 
    
         
             
                  { :parsed_data => response.env[:body], :response => response }
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
       105 
112 
     | 
    
         
             
                end
         
     | 
| 
       106 
113 
     | 
    
         | 
| 
       107 
114 
     | 
    
         
             
                private
         
     | 
| 
         @@ -26,11 +26,7 @@ module Her 
     | 
|
| 
       26 
26 
     | 
    
         
             
                      return {} unless data[data_key]
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
28 
     | 
    
         
             
                      klass = klass.her_nearby_class(association[:class_name])
         
     | 
| 
       29 
     | 
    
         
            -
                       
     | 
| 
       30 
     | 
    
         
            -
                        { association[:name] => data[data_key] }
         
     | 
| 
       31 
     | 
    
         
            -
                      else
         
     | 
| 
       32 
     | 
    
         
            -
                        { association[:name] => klass.new(klass.parse(data[data_key])) }
         
     | 
| 
       33 
     | 
    
         
            -
                      end
         
     | 
| 
      
 29 
     | 
    
         
            +
                      { association[:name] => klass.instantiate_record(klass, data: data[data_key]) }
         
     | 
| 
       34 
30 
     | 
    
         
             
                    end
         
     | 
| 
       35 
31 
     | 
    
         | 
| 
       36 
32 
     | 
    
         
             
                    # @private
         
     | 
| 
         @@ -49,6 +45,7 @@ module Her 
     | 
|
| 
       49 
45 
     | 
    
         | 
| 
       50 
46 
     | 
    
         
             
                      return @cached_result unless @params.any? || @cached_result.nil?
         
     | 
| 
       51 
47 
     | 
    
         
             
                      return @parent.attributes[@name] unless @params.any? || @parent.attributes[@name].blank?
         
     | 
| 
      
 48 
     | 
    
         
            +
                      return @opts[:default].try(:dup) if @parent.new?
         
     | 
| 
       52 
49 
     | 
    
         | 
| 
       53 
50 
     | 
    
         
             
                      path = build_association_path lambda { "#{@parent.request_path(@params)}#{@opts[:path]}" }
         
     | 
| 
       54 
51 
     | 
    
         
             
                      @klass.get(path, @params).tap do |result|
         
     | 
| 
         @@ -65,6 +62,13 @@ module Her 
     | 
|
| 
       65 
62 
     | 
    
         
             
                      end
         
     | 
| 
       66 
63 
     | 
    
         
             
                    end
         
     | 
| 
       67 
64 
     | 
    
         | 
| 
      
 65 
     | 
    
         
            +
                    # @private
         
     | 
| 
      
 66 
     | 
    
         
            +
                    def reset
         
     | 
| 
      
 67 
     | 
    
         
            +
                      @params = {}
         
     | 
| 
      
 68 
     | 
    
         
            +
                      @cached_result = nil
         
     | 
| 
      
 69 
     | 
    
         
            +
                      @parent.attributes.delete(@name)
         
     | 
| 
      
 70 
     | 
    
         
            +
                    end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
       68 
72 
     | 
    
         
             
                    # Add query parameters to the HTTP request performed to fetch the data
         
     | 
| 
       69 
73 
     | 
    
         
             
                    #
         
     | 
| 
       70 
74 
     | 
    
         
             
                    # @example
         
     | 
| 
         @@ -97,6 +101,29 @@ module Her 
     | 
|
| 
       97 
101 
     | 
    
         
             
                      @klass.get_resource(path, @params)
         
     | 
| 
       98 
102 
     | 
    
         
             
                    end
         
     | 
| 
       99 
103 
     | 
    
         | 
| 
      
 104 
     | 
    
         
            +
                    # Refetches the association and puts the proxy back in its initial state,
         
     | 
| 
      
 105 
     | 
    
         
            +
                    # which is unloaded. Cached associations are cleared.
         
     | 
| 
      
 106 
     | 
    
         
            +
                    #
         
     | 
| 
      
 107 
     | 
    
         
            +
                    # @example
         
     | 
| 
      
 108 
     | 
    
         
            +
                    #   class User
         
     | 
| 
      
 109 
     | 
    
         
            +
                    #     include Her::Model
         
     | 
| 
      
 110 
     | 
    
         
            +
                    #     has_many :comments
         
     | 
| 
      
 111 
     | 
    
         
            +
                    #   end
         
     | 
| 
      
 112 
     | 
    
         
            +
                    #
         
     | 
| 
      
 113 
     | 
    
         
            +
                    #   class Comment
         
     | 
| 
      
 114 
     | 
    
         
            +
                    #     include Her::Model
         
     | 
| 
      
 115 
     | 
    
         
            +
                    #   end
         
     | 
| 
      
 116 
     | 
    
         
            +
                    #
         
     | 
| 
      
 117 
     | 
    
         
            +
                    #   user = User.find(1)
         
     | 
| 
      
 118 
     | 
    
         
            +
                    #   user.comments = [#<Comment(comments/2) id=2 body="Hello!">]
         
     | 
| 
      
 119 
     | 
    
         
            +
                    #   user.comments.first.id = "Oops"
         
     | 
| 
      
 120 
     | 
    
         
            +
                    #   user.comments.reload # => [#<Comment(comments/2) id=2 body="Hello!">]
         
     | 
| 
      
 121 
     | 
    
         
            +
                    #   # Fetched again via GET "/users/1/comments"
         
     | 
| 
      
 122 
     | 
    
         
            +
                    def reload
         
     | 
| 
      
 123 
     | 
    
         
            +
                      reset
         
     | 
| 
      
 124 
     | 
    
         
            +
                      fetch
         
     | 
| 
      
 125 
     | 
    
         
            +
                    end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
       100 
127 
     | 
    
         
             
                  end
         
     | 
| 
       101 
128 
     | 
    
         
             
                end
         
     | 
| 
       102 
129 
     | 
    
         
             
              end
         
     | 
| 
         @@ -80,7 +80,7 @@ module Her 
     | 
|
| 
       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 
     | 
    
         
            -
                      path = build_association_path  
     | 
| 
      
 83 
     | 
    
         
            +
                      path = build_association_path -> { @klass.build_request_path(@opts[:path], path_params) }
         
     | 
| 
       84 
84 
     | 
    
         
             
                      @klass.get_resource(path, @params).tap do |result|
         
     | 
| 
       85 
85 
     | 
    
         
             
                        @cached_result = result if @params.blank?
         
     | 
| 
       86 
86 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -31,7 +31,7 @@ module Her 
     | 
|
| 
       31 
31 
     | 
    
         
             
                      return {} unless data[data_key]
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
       33 
33 
     | 
    
         
             
                      klass = klass.her_nearby_class(association[:class_name])
         
     | 
| 
       34 
     | 
    
         
            -
                      { association[:name] =>  
     | 
| 
      
 34 
     | 
    
         
            +
                      { association[:name] => klass.instantiate_collection(klass, :data => data[data_key]) }
         
     | 
| 
       35 
35 
     | 
    
         
             
                    end
         
     | 
| 
       36 
36 
     | 
    
         | 
| 
       37 
37 
     | 
    
         
             
                    # Initialize a new object with a foreign key to the parent
         
     | 
| 
         @@ -85,14 +85,14 @@ module Her 
     | 
|
| 
       85 
85 
     | 
    
         
             
                    def fetch
         
     | 
| 
       86 
86 
     | 
    
         
             
                      super.tap do |o|
         
     | 
| 
       87 
87 
     | 
    
         
             
                        inverse_of = @opts[:inverse_of] || @parent.singularized_resource_name
         
     | 
| 
       88 
     | 
    
         
            -
                        o.each { |entry| entry. 
     | 
| 
      
 88 
     | 
    
         
            +
                        o.each { |entry| entry.attributes[inverse_of] = @parent }
         
     | 
| 
       89 
89 
     | 
    
         
             
                      end
         
     | 
| 
       90 
90 
     | 
    
         
             
                    end
         
     | 
| 
       91 
91 
     | 
    
         | 
| 
       92 
92 
     | 
    
         
             
                    # @private
         
     | 
| 
       93 
93 
     | 
    
         
             
                    def assign_nested_attributes(attributes)
         
     | 
| 
       94 
94 
     | 
    
         
             
                      data = attributes.is_a?(Hash) ? attributes.values : attributes
         
     | 
| 
       95 
     | 
    
         
            -
                      @parent.attributes[@name] =  
     | 
| 
      
 95 
     | 
    
         
            +
                      @parent.attributes[@name] = @klass.instantiate_collection(@klass, :data => data)
         
     | 
| 
       96 
96 
     | 
    
         
             
                    end
         
     | 
| 
       97 
97 
     | 
    
         
             
                  end
         
     | 
| 
       98 
98 
     | 
    
         
             
                end
         
     | 
    
        data/lib/her/model/attributes.rb
    CHANGED
    
    | 
         @@ -16,7 +16,13 @@ module Her 
     | 
|
| 
       16 
16 
     | 
    
         
             
                  #     include Her::Model
         
     | 
| 
       17 
17 
     | 
    
         
             
                  #   end
         
     | 
| 
       18 
18 
     | 
    
         
             
                  #
         
     | 
| 
       19 
     | 
    
         
            -
                  # 
     | 
| 
      
 19 
     | 
    
         
            +
                  #   User.new(name: "Tobias")
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #   # => #<User name="Tobias">
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #   User.new do |u|
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #     u.name = "Tobias"
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #   # => #<User name="Tobias">
         
     | 
| 
       20 
26 
     | 
    
         
             
                  def initialize(attributes={})
         
     | 
| 
       21 
27 
     | 
    
         
             
                    attributes ||= {}
         
     | 
| 
       22 
28 
     | 
    
         
             
                    @metadata = attributes.delete(:_metadata) || {}
         
     | 
| 
         @@ -25,53 +31,15 @@ module Her 
     | 
|
| 
       25 
31 
     | 
    
         | 
| 
       26 
32 
     | 
    
         
             
                    attributes = self.class.default_scope.apply_to(attributes)
         
     | 
| 
       27 
33 
     | 
    
         
             
                    assign_attributes(attributes)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    yield self if block_given?
         
     | 
| 
       28 
35 
     | 
    
         
             
                    run_callbacks :initialize
         
     | 
| 
       29 
36 
     | 
    
         
             
                  end
         
     | 
| 
       30 
37 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
                  # Initialize a collection of resources
         
     | 
| 
       32 
     | 
    
         
            -
                  #
         
     | 
| 
       33 
     | 
    
         
            -
                  # @private
         
     | 
| 
       34 
     | 
    
         
            -
                  def self.initialize_collection(klass, parsed_data={})
         
     | 
| 
       35 
     | 
    
         
            -
                    collection_data = klass.extract_array(parsed_data).map do |item_data|
         
     | 
| 
       36 
     | 
    
         
            -
                      if item_data.kind_of?(klass)
         
     | 
| 
       37 
     | 
    
         
            -
                        resource = item_data
         
     | 
| 
       38 
     | 
    
         
            -
                      else
         
     | 
| 
       39 
     | 
    
         
            -
                        resource = klass.new(klass.parse(item_data))
         
     | 
| 
       40 
     | 
    
         
            -
                        resource.run_callbacks :find
         
     | 
| 
       41 
     | 
    
         
            -
                      end
         
     | 
| 
       42 
     | 
    
         
            -
                      resource
         
     | 
| 
       43 
     | 
    
         
            -
                    end
         
     | 
| 
       44 
     | 
    
         
            -
                    Her::Collection.new(collection_data, parsed_data[:metadata], parsed_data[:errors])
         
     | 
| 
       45 
     | 
    
         
            -
                  end
         
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
                  # Use setter methods of model for each key / value pair in params
         
     | 
| 
       48 
     | 
    
         
            -
                  # Return key / value pairs for which no setter method was defined on the model
         
     | 
| 
       49 
     | 
    
         
            -
                  #
         
     | 
| 
       50 
     | 
    
         
            -
                  # @private
         
     | 
| 
       51 
     | 
    
         
            -
                  def self.use_setter_methods(model, params)
         
     | 
| 
       52 
     | 
    
         
            -
                    params ||= {}
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
                    reserved_keys = [:id, model.class.primary_key] + model.class.association_keys
         
     | 
| 
       55 
     | 
    
         
            -
                    model.class.attributes *params.keys.reject { |k| reserved_keys.include?(k) || reserved_keys.map(&:to_s).include?(k) }
         
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
                    setter_method_names = model.class.setter_method_names
         
     | 
| 
       58 
     | 
    
         
            -
                    params.inject({}) do |memo, (key, value)|
         
     | 
| 
       59 
     | 
    
         
            -
                      setter_method = key.to_s + '='
         
     | 
| 
       60 
     | 
    
         
            -
                      if setter_method_names.include?(setter_method)
         
     | 
| 
       61 
     | 
    
         
            -
                        model.send(setter_method, value)
         
     | 
| 
       62 
     | 
    
         
            -
                      else
         
     | 
| 
       63 
     | 
    
         
            -
                        key = key.to_sym if key.is_a?(String)
         
     | 
| 
       64 
     | 
    
         
            -
                        memo[key] = value
         
     | 
| 
       65 
     | 
    
         
            -
                      end
         
     | 
| 
       66 
     | 
    
         
            -
                      memo
         
     | 
| 
       67 
     | 
    
         
            -
                    end
         
     | 
| 
       68 
     | 
    
         
            -
                  end
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
38 
     | 
    
         
             
                  # Handles missing methods
         
     | 
| 
       71 
39 
     | 
    
         
             
                  #
         
     | 
| 
       72 
40 
     | 
    
         
             
                  # @private
         
     | 
| 
       73 
41 
     | 
    
         
             
                  def method_missing(method, *args, &blk)
         
     | 
| 
       74 
     | 
    
         
            -
                    if method.to_s =~ /[?=]$/ || @ 
     | 
| 
      
 42 
     | 
    
         
            +
                    if method.to_s =~ /[?=]$/ || @_her_attributes.include?(method)
         
     | 
| 
       75 
43 
     | 
    
         
             
                      # Extract the attribute
         
     | 
| 
       76 
44 
     | 
    
         
             
                      attribute = method.to_s.sub(/[?=]$/, '')
         
     | 
| 
       77 
45 
     | 
    
         | 
| 
         @@ -87,7 +55,7 @@ module Her 
     | 
|
| 
       87 
55 
     | 
    
         | 
| 
       88 
56 
     | 
    
         
             
                  # @private
         
     | 
| 
       89 
57 
     | 
    
         
             
                  def respond_to_missing?(method, include_private = false)
         
     | 
| 
       90 
     | 
    
         
            -
                    method.to_s 
     | 
| 
      
 58 
     | 
    
         
            +
                    method.to_s =~ /[?=]$/ || @_her_attributes.include?(method) || super
         
     | 
| 
       91 
59 
     | 
    
         
             
                  end
         
     | 
| 
       92 
60 
     | 
    
         | 
| 
       93 
61 
     | 
    
         
             
                  # Assign new attributes to a resource
         
     | 
| 
         @@ -101,47 +69,55 @@ module Her 
     | 
|
| 
       101 
69 
     | 
    
         
             
                  #   user.assign_attributes(name: "Lindsay")
         
     | 
| 
       102 
70 
     | 
    
         
             
                  #   user.changes # => { :name => ["Tobias", "Lindsay"] }
         
     | 
| 
       103 
71 
     | 
    
         
             
                  def assign_attributes(new_attributes)
         
     | 
| 
       104 
     | 
    
         
            -
                    @ 
     | 
| 
      
 72 
     | 
    
         
            +
                    @_her_attributes ||= attributes
         
     | 
| 
       105 
73 
     | 
    
         
             
                    # Use setter methods first
         
     | 
| 
       106 
     | 
    
         
            -
                    unset_attributes =  
     | 
| 
      
 74 
     | 
    
         
            +
                    unset_attributes = self.class.use_setter_methods(self, new_attributes)
         
     | 
| 
       107 
75 
     | 
    
         | 
| 
       108 
76 
     | 
    
         
             
                    # Then translate attributes of associations into association instances
         
     | 
| 
       109 
     | 
    
         
            -
                     
     | 
| 
      
 77 
     | 
    
         
            +
                    associations = self.class.parse_associations(unset_attributes)
         
     | 
| 
       110 
78 
     | 
    
         | 
| 
       111 
     | 
    
         
            -
                    # Then merge the  
     | 
| 
       112 
     | 
    
         
            -
                    @ 
     | 
| 
      
 79 
     | 
    
         
            +
                    # Then merge the associations into @_her_attributes.
         
     | 
| 
      
 80 
     | 
    
         
            +
                    @_her_attributes.merge!(associations)
         
     | 
| 
       113 
81 
     | 
    
         
             
                  end
         
     | 
| 
       114 
82 
     | 
    
         
             
                  alias attributes= assign_attributes
         
     | 
| 
       115 
83 
     | 
    
         | 
| 
       116 
84 
     | 
    
         
             
                  def attributes
         
     | 
| 
       117 
     | 
    
         
            -
                     
     | 
| 
      
 85 
     | 
    
         
            +
                    # The natural choice of instance variable naming here would be
         
     | 
| 
      
 86 
     | 
    
         
            +
                    # `@attributes`. Unfortunately that causes a naming clash when
         
     | 
| 
      
 87 
     | 
    
         
            +
                    # used with `ActiveModel` version >= 5.2.0.
         
     | 
| 
      
 88 
     | 
    
         
            +
                    # As of v5.2.0 `ActiveModel` checks to see if `ActiveRecord`
         
     | 
| 
      
 89 
     | 
    
         
            +
                    # attributes exist, and assumes that if the instance variable
         
     | 
| 
      
 90 
     | 
    
         
            +
                    # `@attributes` exists on the instance, it is because they are
         
     | 
| 
      
 91 
     | 
    
         
            +
                    # `ActiveRecord` attributes.
         
     | 
| 
      
 92 
     | 
    
         
            +
                    @_her_attributes ||= HashWithIndifferentAccess.new
         
     | 
| 
       118 
93 
     | 
    
         
             
                  end
         
     | 
| 
       119 
94 
     | 
    
         | 
| 
       120 
95 
     | 
    
         
             
                  # Handles returning true for the accessible attributes
         
     | 
| 
       121 
96 
     | 
    
         
             
                  #
         
     | 
| 
       122 
97 
     | 
    
         
             
                  # @private
         
     | 
| 
       123 
98 
     | 
    
         
             
                  def has_attribute?(attribute_name)
         
     | 
| 
       124 
     | 
    
         
            -
                    @ 
     | 
| 
      
 99 
     | 
    
         
            +
                    @_her_attributes.include?(attribute_name)
         
     | 
| 
       125 
100 
     | 
    
         
             
                  end
         
     | 
| 
       126 
101 
     | 
    
         | 
| 
       127 
102 
     | 
    
         
             
                  # Handles returning data for a specific attribute
         
     | 
| 
       128 
103 
     | 
    
         
             
                  #
         
     | 
| 
       129 
104 
     | 
    
         
             
                  # @private
         
     | 
| 
       130 
105 
     | 
    
         
             
                  def get_attribute(attribute_name)
         
     | 
| 
       131 
     | 
    
         
            -
                    @ 
     | 
| 
      
 106 
     | 
    
         
            +
                    @_her_attributes[attribute_name]
         
     | 
| 
       132 
107 
     | 
    
         
             
                  end
         
     | 
| 
       133 
108 
     | 
    
         
             
                  alias attribute get_attribute
         
     | 
| 
       134 
109 
     | 
    
         | 
| 
       135 
110 
     | 
    
         
             
                  # Return the value of the model `primary_key` attribute
         
     | 
| 
       136 
111 
     | 
    
         
             
                  def id
         
     | 
| 
       137 
     | 
    
         
            -
                    @ 
     | 
| 
      
 112 
     | 
    
         
            +
                    @_her_attributes[self.class.primary_key]
         
     | 
| 
       138 
113 
     | 
    
         
             
                  end
         
     | 
| 
       139 
114 
     | 
    
         | 
| 
       140 
     | 
    
         
            -
                  # Return `true` if the other object is also a Her::Model and has matching 
     | 
| 
      
 115 
     | 
    
         
            +
                  # Return `true` if the other object is also a Her::Model and has matching
         
     | 
| 
      
 116 
     | 
    
         
            +
                  # data
         
     | 
| 
       141 
117 
     | 
    
         
             
                  #
         
     | 
| 
       142 
118 
     | 
    
         
             
                  # @private
         
     | 
| 
       143 
119 
     | 
    
         
             
                  def ==(other)
         
     | 
| 
       144 
     | 
    
         
            -
                    other.is_a?(Her::Model) && @ 
     | 
| 
      
 120 
     | 
    
         
            +
                    other.is_a?(Her::Model) && @_her_attributes == other.attributes
         
     | 
| 
       145 
121 
     | 
    
         
             
                  end
         
     | 
| 
       146 
122 
     | 
    
         | 
| 
       147 
123 
     | 
    
         
             
                  # Delegate to the == method
         
     | 
| 
         @@ -151,47 +127,94 @@ module Her 
     | 
|
| 
       151 
127 
     | 
    
         
             
                    self == other
         
     | 
| 
       152 
128 
     | 
    
         
             
                  end
         
     | 
| 
       153 
129 
     | 
    
         | 
| 
       154 
     | 
    
         
            -
                  # Delegate to @ 
     | 
| 
      
 130 
     | 
    
         
            +
                  # Delegate to @_her_attributes, allowing models to act correctly in code like:
         
     | 
| 
       155 
131 
     | 
    
         
             
                  #     [ Model.find(1), Model.find(1) ].uniq # => [ Model.find(1) ]
         
     | 
| 
       156 
132 
     | 
    
         
             
                  # @private
         
     | 
| 
       157 
133 
     | 
    
         
             
                  def hash
         
     | 
| 
       158 
     | 
    
         
            -
                    @ 
     | 
| 
      
 134 
     | 
    
         
            +
                    @_her_attributes.hash
         
     | 
| 
       159 
135 
     | 
    
         
             
                  end
         
     | 
| 
       160 
136 
     | 
    
         | 
| 
       161 
137 
     | 
    
         
             
                  # Assign attribute value (ActiveModel convention method).
         
     | 
| 
       162 
138 
     | 
    
         
             
                  #
         
     | 
| 
       163 
139 
     | 
    
         
             
                  # @private
         
     | 
| 
       164 
140 
     | 
    
         
             
                  def attribute=(attribute, value)
         
     | 
| 
       165 
     | 
    
         
            -
                    @ 
     | 
| 
       166 
     | 
    
         
            -
                     
     | 
| 
       167 
     | 
    
         
            -
                    @ 
     | 
| 
      
 141 
     | 
    
         
            +
                    @_her_attributes[attribute] = nil unless @_her_attributes.include?(attribute)
         
     | 
| 
      
 142 
     | 
    
         
            +
                    send("#{attribute}_will_change!") unless value == @_her_attributes[attribute]
         
     | 
| 
      
 143 
     | 
    
         
            +
                    @_her_attributes[attribute] = value
         
     | 
| 
       168 
144 
     | 
    
         
             
                  end
         
     | 
| 
       169 
145 
     | 
    
         | 
| 
       170 
146 
     | 
    
         
             
                  # Check attribute value to be present (ActiveModel convention method).
         
     | 
| 
       171 
147 
     | 
    
         
             
                  #
         
     | 
| 
       172 
148 
     | 
    
         
             
                  # @private
         
     | 
| 
       173 
149 
     | 
    
         
             
                  def attribute?(attribute)
         
     | 
| 
       174 
     | 
    
         
            -
                    @ 
     | 
| 
      
 150 
     | 
    
         
            +
                    @_her_attributes.include?(attribute) && @_her_attributes[attribute].present?
         
     | 
| 
       175 
151 
     | 
    
         
             
                  end
         
     | 
| 
       176 
152 
     | 
    
         | 
| 
       177 
153 
     | 
    
         
             
                  module ClassMethods
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                    # Initialize a single resource
         
     | 
| 
      
 156 
     | 
    
         
            +
                    #
         
     | 
| 
      
 157 
     | 
    
         
            +
                    # @private
         
     | 
| 
      
 158 
     | 
    
         
            +
                    def instantiate_record(klass, parsed_data)
         
     | 
| 
      
 159 
     | 
    
         
            +
                      if record = parsed_data[:data] and record.kind_of?(klass)
         
     | 
| 
      
 160 
     | 
    
         
            +
                        record
         
     | 
| 
      
 161 
     | 
    
         
            +
                      else
         
     | 
| 
      
 162 
     | 
    
         
            +
                        attributes = klass.parse(record).merge(_metadata: parsed_data[:metadata],
         
     | 
| 
      
 163 
     | 
    
         
            +
                                                               _errors: parsed_data[:errors])
         
     | 
| 
      
 164 
     | 
    
         
            +
                        klass.new(attributes).tap do |record|
         
     | 
| 
      
 165 
     | 
    
         
            +
                          record.instance_variable_set(:@changed_attributes, {})
         
     | 
| 
      
 166 
     | 
    
         
            +
                          record.run_callbacks :find
         
     | 
| 
      
 167 
     | 
    
         
            +
                        end
         
     | 
| 
      
 168 
     | 
    
         
            +
                      end
         
     | 
| 
      
 169 
     | 
    
         
            +
                    end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                    # Initialize a collection of resources
         
     | 
| 
      
 172 
     | 
    
         
            +
                    #
         
     | 
| 
      
 173 
     | 
    
         
            +
                    # @private
         
     | 
| 
      
 174 
     | 
    
         
            +
                    def instantiate_collection(klass, parsed_data = {})
         
     | 
| 
      
 175 
     | 
    
         
            +
                      records = klass.extract_array(parsed_data).map do |record|
         
     | 
| 
      
 176 
     | 
    
         
            +
                        instantiate_record(klass, data: record)
         
     | 
| 
      
 177 
     | 
    
         
            +
                      end
         
     | 
| 
      
 178 
     | 
    
         
            +
                      Her::Collection.new(records, parsed_data[:metadata], parsed_data[:errors])
         
     | 
| 
      
 179 
     | 
    
         
            +
                    end
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
       178 
181 
     | 
    
         
             
                    # Initialize a collection of resources with raw data from an HTTP request
         
     | 
| 
       179 
182 
     | 
    
         
             
                    #
         
     | 
| 
       180 
183 
     | 
    
         
             
                    # @param [Array] parsed_data
         
     | 
| 
       181 
184 
     | 
    
         
             
                    # @private
         
     | 
| 
       182 
185 
     | 
    
         
             
                    def new_collection(parsed_data)
         
     | 
| 
       183 
     | 
    
         
            -
                       
     | 
| 
      
 186 
     | 
    
         
            +
                      instantiate_collection(self, parsed_data)
         
     | 
| 
       184 
187 
     | 
    
         
             
                    end
         
     | 
| 
       185 
188 
     | 
    
         | 
| 
       186 
189 
     | 
    
         
             
                    # Initialize a new object with the "raw" parsed_data from the parsing middleware
         
     | 
| 
       187 
190 
     | 
    
         
             
                    #
         
     | 
| 
       188 
191 
     | 
    
         
             
                    # @private
         
     | 
| 
       189 
192 
     | 
    
         
             
                    def new_from_parsed_data(parsed_data)
         
     | 
| 
       190 
     | 
    
         
            -
                       
     | 
| 
       191 
     | 
    
         
            -
             
     | 
| 
      
 193 
     | 
    
         
            +
                      instantiate_record(self, parsed_data)
         
     | 
| 
      
 194 
     | 
    
         
            +
                    end
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                    # Use setter methods of model for each key / value pair in params
         
     | 
| 
      
 197 
     | 
    
         
            +
                    # Return key / value pairs for which no setter method was defined on the
         
     | 
| 
      
 198 
     | 
    
         
            +
                    # model
         
     | 
| 
      
 199 
     | 
    
         
            +
                    #
         
     | 
| 
      
 200 
     | 
    
         
            +
                    # @private
         
     | 
| 
      
 201 
     | 
    
         
            +
                    def use_setter_methods(model, params = {})
         
     | 
| 
      
 202 
     | 
    
         
            +
                      reserved = [:id, model.class.primary_key, *model.class.association_keys]
         
     | 
| 
      
 203 
     | 
    
         
            +
                      model.class.attributes *params.keys.reject { |k| reserved.include?(k) }
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                      setter_method_names = model.class.setter_method_names
         
     | 
| 
      
 206 
     | 
    
         
            +
                      params.each_with_object({}) do |(key, value), memo|
         
     | 
| 
      
 207 
     | 
    
         
            +
                        setter_method = "#{key}="
         
     | 
| 
      
 208 
     | 
    
         
            +
                        if setter_method_names.include?(setter_method)
         
     | 
| 
      
 209 
     | 
    
         
            +
                          model.send setter_method, value
         
     | 
| 
      
 210 
     | 
    
         
            +
                        else
         
     | 
| 
      
 211 
     | 
    
         
            +
                          memo[key.to_sym] = value
         
     | 
| 
      
 212 
     | 
    
         
            +
                        end
         
     | 
| 
      
 213 
     | 
    
         
            +
                      end
         
     | 
| 
       192 
214 
     | 
    
         
             
                    end
         
     | 
| 
       193 
215 
     | 
    
         | 
| 
       194 
     | 
    
         
            -
                    # Define attribute method matchers to automatically define them using 
     | 
| 
      
 216 
     | 
    
         
            +
                    # Define attribute method matchers to automatically define them using
         
     | 
| 
      
 217 
     | 
    
         
            +
                    # ActiveModel's define_attribute_methods.
         
     | 
| 
       195 
218 
     | 
    
         
             
                    #
         
     | 
| 
       196 
219 
     | 
    
         
             
                    # @private
         
     | 
| 
       197 
220 
     | 
    
         
             
                    def define_attribute_method_matchers
         
     | 
| 
         @@ -199,18 +222,22 @@ module Her 
     | 
|
| 
       199 
222 
     | 
    
         
             
                      attribute_method_suffix '?'
         
     | 
| 
       200 
223 
     | 
    
         
             
                    end
         
     | 
| 
       201 
224 
     | 
    
         | 
| 
       202 
     | 
    
         
            -
                    # Create a mutex for dynamically generated attribute methods or use one 
     | 
| 
      
 225 
     | 
    
         
            +
                    # Create a mutex for dynamically generated attribute methods or use one
         
     | 
| 
      
 226 
     | 
    
         
            +
                    # defined by ActiveModel.
         
     | 
| 
       203 
227 
     | 
    
         
             
                    #
         
     | 
| 
       204 
228 
     | 
    
         
             
                    # @private
         
     | 
| 
       205 
229 
     | 
    
         
             
                    def attribute_methods_mutex
         
     | 
| 
       206 
     | 
    
         
            -
                      @attribute_methods_mutex ||=  
     | 
| 
       207 
     | 
    
         
            -
             
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
             
     | 
| 
       210 
     | 
    
         
            -
             
     | 
| 
      
 230 
     | 
    
         
            +
                      @attribute_methods_mutex ||= begin
         
     | 
| 
      
 231 
     | 
    
         
            +
                        if generated_attribute_methods.respond_to? :mu_synchronize
         
     | 
| 
      
 232 
     | 
    
         
            +
                          generated_attribute_methods
         
     | 
| 
      
 233 
     | 
    
         
            +
                        else
         
     | 
| 
      
 234 
     | 
    
         
            +
                          Mutex.new
         
     | 
| 
      
 235 
     | 
    
         
            +
                        end
         
     | 
| 
      
 236 
     | 
    
         
            +
                      end
         
     | 
| 
       211 
237 
     | 
    
         
             
                    end
         
     | 
| 
       212 
238 
     | 
    
         | 
| 
       213 
     | 
    
         
            -
                    # Define the attributes that will be used to track dirty attributes and 
     | 
| 
      
 239 
     | 
    
         
            +
                    # Define the attributes that will be used to track dirty attributes and
         
     | 
| 
      
 240 
     | 
    
         
            +
                    # validations
         
     | 
| 
       214 
241 
     | 
    
         
             
                    #
         
     | 
| 
       215 
242 
     | 
    
         
             
                    # @param [Array] attributes
         
     | 
| 
       216 
243 
     | 
    
         
             
                    # @example
         
     | 
| 
         @@ -224,7 +251,8 @@ module Her 
     | 
|
| 
       224 
251 
     | 
    
         
             
                      end
         
     | 
| 
       225 
252 
     | 
    
         
             
                    end
         
     | 
| 
       226 
253 
     | 
    
         | 
| 
       227 
     | 
    
         
            -
                    # Define the accessor in which the API response errors (obtained from 
     | 
| 
      
 254 
     | 
    
         
            +
                    # Define the accessor in which the API response errors (obtained from
         
     | 
| 
      
 255 
     | 
    
         
            +
                    # the parsing middleware) will be stored
         
     | 
| 
       228 
256 
     | 
    
         
             
                    #
         
     | 
| 
       229 
257 
     | 
    
         
             
                    # @param [Symbol] store_response_errors
         
     | 
| 
       230 
258 
     | 
    
         
             
                    #
         
     | 
| 
         @@ -237,7 +265,8 @@ module Her 
     | 
|
| 
       237 
265 
     | 
    
         
             
                      store_her_data(:response_errors, value)
         
     | 
| 
       238 
266 
     | 
    
         
             
                    end
         
     | 
| 
       239 
267 
     | 
    
         | 
| 
       240 
     | 
    
         
            -
                    # Define the accessor in which the API response metadata (obtained from 
     | 
| 
      
 268 
     | 
    
         
            +
                    # Define the accessor in which the API response metadata (obtained from
         
     | 
| 
      
 269 
     | 
    
         
            +
                    # the parsing middleware) will be stored
         
     | 
| 
       241 
270 
     | 
    
         
             
                    #
         
     | 
| 
       242 
271 
     | 
    
         
             
                    # @param [Symbol] store_metadata
         
     | 
| 
       243 
272 
     | 
    
         
             
                    #
         
     | 
| 
         @@ -252,9 +281,10 @@ module Her 
     | 
|
| 
       252 
281 
     | 
    
         | 
| 
       253 
282 
     | 
    
         
             
                    # @private
         
     | 
| 
       254 
283 
     | 
    
         
             
                    def setter_method_names
         
     | 
| 
       255 
     | 
    
         
            -
                      @_her_setter_method_names ||=  
     | 
| 
       256 
     | 
    
         
            -
                         
     | 
| 
       257 
     | 
    
         
            -
             
     | 
| 
      
 284 
     | 
    
         
            +
                      @_her_setter_method_names ||= begin
         
     | 
| 
      
 285 
     | 
    
         
            +
                        instance_methods.each_with_object(Set.new) do |method, memo|
         
     | 
| 
      
 286 
     | 
    
         
            +
                          memo << method.to_s if method.to_s.end_with?('=')
         
     | 
| 
      
 287 
     | 
    
         
            +
                        end
         
     | 
| 
       258 
288 
     | 
    
         
             
                      end
         
     | 
| 
       259 
289 
     | 
    
         
             
                    end
         
     | 
| 
       260 
290 
     | 
    
         | 
    
        data/lib/her/model/http.rb
    CHANGED
    
    | 
         @@ -3,7 +3,7 @@ module Her 
     | 
|
| 
       3 
3 
     | 
    
         
             
                # This module interacts with Her::API to fetch HTTP data
         
     | 
| 
       4 
4 
     | 
    
         
             
                module HTTP
         
     | 
| 
       5 
5 
     | 
    
         
             
                  extend ActiveSupport::Concern
         
     | 
| 
       6 
     | 
    
         
            -
                  METHODS = [:get, :post, :put, :patch, :delete]
         
     | 
| 
      
 6 
     | 
    
         
            +
                  METHODS = [:get, :post, :put, :patch, :delete, :options]
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                  # For each HTTP method, define these class methods:
         
     | 
| 
       9 
9 
     | 
    
         
             
                  #
         
     | 
| 
         @@ -71,7 +71,7 @@ module Her 
     | 
|
| 
       71 
71 
     | 
    
         
             
                            if parsed_data[:data].is_a?(Array) || active_model_serializers_format? || json_api_format?
         
     | 
| 
       72 
72 
     | 
    
         
             
                              new_collection(parsed_data)
         
     | 
| 
       73 
73 
     | 
    
         
             
                            else
         
     | 
| 
       74 
     | 
    
         
            -
                               
     | 
| 
      
 74 
     | 
    
         
            +
                              new_from_parsed_data(parsed_data)
         
     | 
| 
       75 
75 
     | 
    
         
             
                            end
         
     | 
| 
       76 
76 
     | 
    
         
             
                          end
         
     | 
| 
       77 
77 
     | 
    
         
             
                        end
         
     | 
| 
         @@ -91,7 +91,7 @@ module Her 
     | 
|
| 
       91 
91 
     | 
    
         
             
                        def #{method}_resource(path, params={})
         
     | 
| 
       92 
92 
     | 
    
         
             
                          path = build_request_path_from_string_or_symbol(path, params)
         
     | 
| 
       93 
93 
     | 
    
         
             
                          send(:"#{method}_raw", path, params) do |parsed_data, response|
         
     | 
| 
       94 
     | 
    
         
            -
                             
     | 
| 
      
 94 
     | 
    
         
            +
                            new_from_parsed_data(parsed_data)
         
     | 
| 
       95 
95 
     | 
    
         
             
                          end
         
     | 
| 
       96 
96 
     | 
    
         
             
                        end
         
     | 
| 
       97 
97 
     | 
    
         |