requisite 0.4.1 → 0.4.3
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/.gitignore +2 -1
- data/README.md +25 -5
- data/lib/requisite/boundary_object.rb +26 -16
- data/lib/requisite/version.rb +1 -1
- data/test/requisite/api_model_test.rb +64 -53
- data/test/requisite/api_user_test.rb +42 -9
- metadata +6 -7
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 828a5465ab9ec59c47bda9e6ab72c34e9bdbab05536ab99ad45c3cdaec933f16
         | 
| 4 | 
            +
              data.tar.gz: b1c387023439e720d6600310604828d672abd8dd4eabb25d0412bcec423c4529
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: eb0c6e6e4cc10fb351e01334754196f2a65ced999a32980f416a2c226b4ea1a76a77b0a1b0523e408de9b372fc40676ab7e9482290f28d13d983947ae750a9f9
         | 
| 7 | 
            +
              data.tar.gz: 3269605161e8264e04dc0640637a107d4db5ef079f635e31c674cc35cb409eb32ccb991a3f17bcebe4f31bedcd6616fbfb09856829a86f5a802f67f998df9ac1
         | 
    
        data/.gitignore
    CHANGED
    
    | @@ -1 +1,2 @@ | |
| 1 | 
            -
            *.gem
         | 
| 1 | 
            +
            *.gem
         | 
| 2 | 
            +
            Gemfile.lock
         | 
    
        data/README.md
    CHANGED
    
    | @@ -38,7 +38,7 @@ class UserApiModel < Requisite::ApiModel | |
| 38 38 | 
             
                attribute! :username
         | 
| 39 39 | 
             
                attribute :real_name
         | 
| 40 40 | 
             
              end
         | 
| 41 | 
            -
             | 
| 41 | 
            +
             | 
| 42 42 | 
             
              # method with the name of of an attribute will be called to calculate the mapped value
         | 
| 43 43 | 
             
              def real_name
         | 
| 44 44 | 
             
                "#{attribute_from_model(:first_name)} #{attribute_from_model(:last_name)}"
         | 
| @@ -88,10 +88,10 @@ Example: | |
| 88 88 | 
             
            class UserApiModel < Requisite::ApiModel
         | 
| 89 89 | 
             
              serialized_attributes do
         | 
| 90 90 | 
             
                attribute :id, stringify: true
         | 
| 91 | 
            -
                attribute :custom_attributes, rename: :custom_data | 
| 91 | 
            +
                attribute :custom_attributes, rename: :custom_data
         | 
| 92 92 | 
             
                attribute :is_awesome, default: true
         | 
| 93 93 | 
             
                attribute :awesome_score, rename: :score, stringify: true, default: 9001
         | 
| 94 | 
            -
                attribute :age, type:  | 
| 94 | 
            +
                attribute :age, type: Integer,
         | 
| 95 95 | 
             
                attribute :tired, type: Requisite::Boolean
         | 
| 96 96 | 
             
              end
         | 
| 97 97 | 
             
            end
         | 
| @@ -131,7 +131,7 @@ With typed hashes, only values specified with a type are permitted: | |
| 131 131 | 
             
            ```ruby
         | 
| 132 132 | 
             
            class UserApiModel < Requisite::ApiModel
         | 
| 133 133 | 
             
              serialized_attributes do
         | 
| 134 | 
            -
                attribute :data, typed_hash: { is_awesome: Requisite::Boolean, score:  | 
| 134 | 
            +
                attribute :data, typed_hash: { is_awesome: Requisite::Boolean, score: Integer, name: String  }
         | 
| 135 135 | 
             
              end
         | 
| 136 136 | 
             
            end
         | 
| 137 137 |  | 
| @@ -198,7 +198,27 @@ class ApiUser < Requisite::ApiModel | |
| 198 198 | 
             
                raise IdentifierNotFoundError unless identifier
         | 
| 199 199 | 
             
              end
         | 
| 200 200 | 
             
            end
         | 
| 201 | 
            -
            ``` | 
| 201 | 
            +
            ```
         | 
| 202 | 
            +
             | 
| 203 | 
            +
            #### Around each attribute
         | 
| 204 | 
            +
             | 
| 205 | 
            +
            An `around_each_attribute` method can be defined to wrap each attribute fetch in a block. This can be useful for instrumenting processing on a per attribute basis.
         | 
| 206 | 
            +
             | 
| 207 | 
            +
            ```ruby
         | 
| 208 | 
            +
            class ApiUser < Requisite::ApiModel
         | 
| 209 | 
            +
              serialized_attributes do
         | 
| 210 | 
            +
                attribute :id, type: String
         | 
| 211 | 
            +
                attribute :email, type: String
         | 
| 212 | 
            +
              end
         | 
| 213 | 
            +
             | 
| 214 | 
            +
              def around_each_attribute(name, &block)
         | 
| 215 | 
            +
                start = Time.now
         | 
| 216 | 
            +
                yield
         | 
| 217 | 
            +
                end = Time.now
         | 
| 218 | 
            +
                puts "Fetching #{name} took #{end - start}"
         | 
| 219 | 
            +
              end
         | 
| 220 | 
            +
            end
         | 
| 221 | 
            +
            ```
         | 
| 202 222 |  | 
| 203 223 | 
             
            #### Thanks
         | 
| 204 224 |  | 
| @@ -4,14 +4,17 @@ module Requisite | |
| 4 4 | 
             
                  def attribute(name, options={})
         | 
| 5 5 | 
             
                    attribute_keys << name
         | 
| 6 6 | 
             
                    define_method(name) do
         | 
| 7 | 
            -
                       | 
| 8 | 
            -
                       | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 7 | 
            +
                      result = nil
         | 
| 8 | 
            +
                      self.send(:around_each_attribute, name) do
         | 
| 9 | 
            +
                        resolved_name = options[:rename] || name
         | 
| 10 | 
            +
                        result = self.send(:convert, resolved_name)
         | 
| 11 | 
            +
                        result = self.send(:parse_typed_hash, resolved_name, options[:typed_hash]) if options[:typed_hash]
         | 
| 12 | 
            +
                        result = self.send(:parse_scalar_hash, resolved_name) if options[:scalar_hash]
         | 
| 13 | 
            +
                        result = self.send(:parse_typed_array, resolved_name, options[:typed_array]) if options[:typed_array]
         | 
| 14 | 
            +
                        result = options[:default] if (options.key?(:default) && empty_result?(result))
         | 
| 15 | 
            +
                        raise_bad_type_if_type_mismatch(result, options[:type]) if options[:type] && result
         | 
| 16 | 
            +
                        result = result.to_s if options[:stringify]
         | 
| 17 | 
            +
                      end
         | 
| 15 18 | 
             
                      result
         | 
| 16 19 | 
             
                    end
         | 
| 17 20 | 
             
                  end
         | 
| @@ -19,13 +22,16 @@ module Requisite | |
| 19 22 | 
             
                  def attribute!(name, options={})
         | 
| 20 23 | 
             
                    attribute_keys << name
         | 
| 21 24 | 
             
                    define_method(name) do
         | 
| 22 | 
            -
                       | 
| 23 | 
            -
                       | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 25 | 
            +
                      result = nil
         | 
| 26 | 
            +
                      self.send(:around_each_attribute, name) do
         | 
| 27 | 
            +
                        resolved_name = options[:rename] || name
         | 
| 28 | 
            +
                        result = self.send(:convert!, resolved_name)
         | 
| 29 | 
            +
                        result = self.send(:parse_typed_hash, resolved_name, options[:typed_hash]) if options[:typed_hash]
         | 
| 30 | 
            +
                        result = self.send(:parse_scalar_hash, resolved_name) if options[:scalar_hash]
         | 
| 31 | 
            +
                        result = self.send(:parse_typed_array, resolved_name, options[:typed_array]) if options[:typed_array]
         | 
| 32 | 
            +
                        result = result.to_s if options[:stringify]
         | 
| 33 | 
            +
                        raise_bad_type_if_type_mismatch(result, options[:type]) if options[:type]
         | 
| 34 | 
            +
                      end
         | 
| 29 35 | 
             
                      result
         | 
| 30 36 | 
             
                    end
         | 
| 31 37 | 
             
                  end
         | 
| @@ -46,11 +52,15 @@ module Requisite | |
| 46 52 |  | 
| 47 53 | 
             
                private
         | 
| 48 54 |  | 
| 55 | 
            +
                def around_each_attribute(name)
         | 
| 56 | 
            +
                  yield
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 49 59 | 
             
                self.singleton_class.send(:alias_method, :a, :attribute)
         | 
| 50 60 | 
             
                self.singleton_class.send(:alias_method, :a!, :attribute!)
         | 
| 51 61 |  | 
| 52 62 | 
             
                def raise_bad_type_if_type_mismatch(value, desired_type)
         | 
| 53 | 
            -
                  raise BadTypeError.new(value, desired_type) unless (value.kind_of?(desired_type)) || ((value.kind_of?(TrueClass) || value.kind_of?( | 
| 63 | 
            +
                  raise BadTypeError.new(value, desired_type) unless (value.kind_of?(desired_type)) || ((value.kind_of?(TrueClass) || value.kind_of?(FalseClass)) && desired_type == Requisite::Boolean)
         | 
| 54 64 | 
             
                end
         | 
| 55 65 |  | 
| 56 66 | 
             
                def raise_not_implemented_for_attribute(name)
         | 
    
        data/lib/requisite/version.rb
    CHANGED
    
    
| @@ -1,5 +1,4 @@ | |
| 1 1 | 
             
            require 'test_helper'
         | 
| 2 | 
            -
            # require_relative '../dummy_api_model'
         | 
| 3 2 |  | 
| 4 3 | 
             
            module Requisite
         | 
| 5 4 | 
             
              class DummyApiModel < Requisite::ApiModel
         | 
| @@ -11,16 +10,16 @@ module Requisite | |
| 11 10 | 
             
                  response = DummyApiModel.new
         | 
| 12 11 | 
             
                  def response.a; 'A'; end
         | 
| 13 12 | 
             
                  def response.b; 2; end
         | 
| 14 | 
            -
                  response.to_hash.must_equal( :a => 'A', :b => 2 )
         | 
| 15 | 
            -
                  response.must_respond_to :a
         | 
| 16 | 
            -
                  response.must_respond_to :b
         | 
| 13 | 
            +
                  _(response.to_hash).must_equal( :a => 'A', :b => 2 )
         | 
| 14 | 
            +
                  _(response).must_respond_to :a
         | 
| 15 | 
            +
                  _(response).must_respond_to :b
         | 
| 17 16 | 
             
                end
         | 
| 18 17 |  | 
| 19 18 | 
             
                it 'attribute provides a default implementation of calling a hash model' do
         | 
| 20 19 | 
             
                  DummyApiModel.serialized_attributes { attribute :c }
         | 
| 21 20 | 
             
                  mock = {:c => 'C'}
         | 
| 22 21 | 
             
                  response = DummyApiModel.new(mock)
         | 
| 23 | 
            -
                  response.to_hash.must_equal( :c => 'C' )
         | 
| 22 | 
            +
                  _(response.to_hash).must_equal( :c => 'C' )
         | 
| 24 23 | 
             
                end
         | 
| 25 24 |  | 
| 26 25 | 
             
                let(:params_hash) { {:c => 'C', :num => 12} }
         | 
| @@ -28,89 +27,101 @@ module Requisite | |
| 28 27 | 
             
                it 'attribute provides a default implementation of calling a model' do
         | 
| 29 28 | 
             
                  DummyApiModel.serialized_attributes { attribute :c }
         | 
| 30 29 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 31 | 
            -
                  response.to_hash.must_equal(:c => 'C')
         | 
| 30 | 
            +
                  _(response.to_hash).must_equal(:c => 'C')
         | 
| 32 31 | 
             
                end
         | 
| 33 32 |  | 
| 34 33 | 
             
                it 'attribute can work with a default' do
         | 
| 35 34 | 
             
                  DummyApiModel.serialized_attributes { attribute :c, default: 'see' }
         | 
| 36 35 | 
             
                  response = DummyApiModel.new
         | 
| 37 | 
            -
                  response.to_hash.must_equal(:c => 'see')
         | 
| 36 | 
            +
                  _(response.to_hash).must_equal(:c => 'see')
         | 
| 38 37 | 
             
                end
         | 
| 39 38 |  | 
| 40 39 | 
             
                it 'ignores default if value given' do
         | 
| 41 40 | 
             
                  DummyApiModel.serialized_attributes { attribute :num, default: 0 }
         | 
| 42 41 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 43 | 
            -
                  response.to_hash.must_equal(:num => 12)
         | 
| 42 | 
            +
                  _(response.to_hash).must_equal(:num => 12)
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                it 'can set a default value of true for a boolean attribute' do
         | 
| 46 | 
            +
                  DummyApiModel.serialized_attributes { attribute :truthy_val, type: Requisite::Boolean, default: true }
         | 
| 47 | 
            +
                  response = DummyApiModel.new
         | 
| 48 | 
            +
                  _(response.to_hash).must_equal(:truthy_val => true)
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                it 'can set a default value of false for a boolean attribute' do
         | 
| 52 | 
            +
                  DummyApiModel.serialized_attributes { attribute :truthy_val, type: Requisite::Boolean, default: false }
         | 
| 53 | 
            +
                  response = DummyApiModel.new
         | 
| 54 | 
            +
                  _(response.to_hash).must_equal(:truthy_val => false)
         | 
| 44 55 | 
             
                end
         | 
| 45 56 |  | 
| 46 57 | 
             
                it 'attribute can be set to stringify fields' do
         | 
| 47 58 | 
             
                  DummyApiModel.serialized_attributes { attribute :num, stringify: true }
         | 
| 48 59 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 49 | 
            -
                  response.to_hash.must_equal(:num => '12')
         | 
| 60 | 
            +
                  _(response.to_hash).must_equal(:num => '12')
         | 
| 50 61 | 
             
                end
         | 
| 51 62 |  | 
| 52 63 | 
             
                it 'attribute can be set to rename fields' do
         | 
| 53 64 | 
             
                  DummyApiModel.serialized_attributes { attribute :my_num, rename: :num }
         | 
| 54 65 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 55 | 
            -
                  response.to_hash.must_equal(:my_num => 12)
         | 
| 66 | 
            +
                  _(response.to_hash).must_equal(:my_num => 12)
         | 
| 56 67 | 
             
                end
         | 
| 57 68 |  | 
| 58 69 | 
             
                it 'attribute can assert type of a field' do
         | 
| 59 70 | 
             
                  DummyApiModel.serialized_attributes { attribute :num, type: String }
         | 
| 60 71 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 61 | 
            -
                  proc { response.to_hash }.must_raise(BadTypeError)
         | 
| 72 | 
            +
                  _(proc { response.to_hash }).must_raise(BadTypeError)
         | 
| 62 73 | 
             
                end
         | 
| 63 74 |  | 
| 64 75 | 
             
                it 'with_type! helper raises on mismatched type' do
         | 
| 65 76 | 
             
                  model = DummyApiModel.new()
         | 
| 66 | 
            -
                  proc { model.send(:with_type!, String) { 1 + 2 }}.must_raise(Requisite::BadTypeError)
         | 
| 77 | 
            +
                  _(proc { model.send(:with_type!, String) { 1 + 2 }}).must_raise(Requisite::BadTypeError)
         | 
| 67 78 | 
             
                end
         | 
| 68 79 |  | 
| 69 80 | 
             
                it 'first_attribute_from_model helper finds first matching attriubute' do
         | 
| 70 81 | 
             
                  model = DummyApiModel.new(:oh => 12, :a => nil, :b => 'B', :c => 'C')
         | 
| 71 | 
            -
                  model.send(:first_attribute_from_model, :a, :b, :c).must_equal('B')
         | 
| 82 | 
            +
                  _(model.send(:first_attribute_from_model, :a, :b, :c)).must_equal('B')
         | 
| 72 83 | 
             
                end
         | 
| 73 84 |  | 
| 74 85 | 
             
                it 'attribute can assert type of a boolean field' do
         | 
| 75 86 | 
             
                  DummyApiModel.serialized_attributes { attribute :truthy_val, type: Requisite::Boolean }
         | 
| 76 87 | 
             
                  response = DummyApiModel.new(:truthy_val => false)
         | 
| 77 | 
            -
                  response.to_hash.must_equal(:truthy_val => false)
         | 
| 88 | 
            +
                  _(response.to_hash).must_equal(:truthy_val => false)
         | 
| 78 89 | 
             
                end
         | 
| 79 90 |  | 
| 80 91 | 
             
                it 'attribute does not include values of nil' do
         | 
| 81 92 | 
             
                  DummyApiModel.serialized_attributes { attribute :num, type: String }
         | 
| 82 93 | 
             
                  response = DummyApiModel.new({:num => nil})
         | 
| 83 | 
            -
                  response.to_hash.must_equal({})
         | 
| 94 | 
            +
                  _(response.to_hash).must_equal({})
         | 
| 84 95 | 
             
                end
         | 
| 85 96 |  | 
| 86 97 | 
             
                it 'attribute includes values of nil if permitted' do
         | 
| 87 98 | 
             
                  DummyApiModel.serialized_attributes { attribute :num, type: String }
         | 
| 88 99 | 
             
                  response = DummyApiModel.new({:num => nil})
         | 
| 89 | 
            -
                  response.to_hash(show_nil: true).must_equal({:num => nil})
         | 
| 100 | 
            +
                  _(response.to_hash(show_nil: true)).must_equal({:num => nil})
         | 
| 90 101 | 
             
                end
         | 
| 91 102 |  | 
| 92 103 | 
             
                it 'attribute can be stringified and renamed with default fields' do
         | 
| 93 104 | 
             
                  DummyApiModel.serialized_attributes { attribute :my_num, rename: :num, stringify: true, default: 22 }
         | 
| 94 105 | 
             
                  response = DummyApiModel.new
         | 
| 95 | 
            -
                  response.to_hash.must_equal(:my_num => '22')
         | 
| 106 | 
            +
                  _(response.to_hash).must_equal(:my_num => '22')
         | 
| 96 107 | 
             
                end
         | 
| 97 108 |  | 
| 98 109 | 
             
                it 'attribute can be stringified after type check' do
         | 
| 99 | 
            -
                  DummyApiModel.serialized_attributes { attribute :num, stringify: true, type:  | 
| 110 | 
            +
                  DummyApiModel.serialized_attributes { attribute :num, stringify: true, type: Integer }
         | 
| 100 111 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 101 | 
            -
                  response.to_hash.must_equal(:num => '12')
         | 
| 112 | 
            +
                  _(response.to_hash).must_equal(:num => '12')
         | 
| 102 113 | 
             
                end
         | 
| 103 114 |  | 
| 104 115 | 
             
                it 'attribute type checks after rename' do
         | 
| 105 116 | 
             
                  DummyApiModel.serialized_attributes { attribute :my_num, rename: :num, type: String }
         | 
| 106 117 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 107 | 
            -
                  proc { response.to_hash }.must_raise(BadTypeError)
         | 
| 118 | 
            +
                  _(proc { response.to_hash }).must_raise(BadTypeError)
         | 
| 108 119 | 
             
                end
         | 
| 109 120 |  | 
| 110 121 | 
             
                it 'attribute can be stringified, renamed, defaulted and have type checking on a field' do
         | 
| 111 122 | 
             
                  DummyApiModel.serialized_attributes { attribute :my_num, rename: :num, stringify: true, default: 22, type: String }
         | 
| 112 123 | 
             
                  response = DummyApiModel.new
         | 
| 113 | 
            -
                  proc { response.to_hash }.must_raise(BadTypeError)
         | 
| 124 | 
            +
                  _(proc { response.to_hash }).must_raise(BadTypeError)
         | 
| 114 125 | 
             
                end
         | 
| 115 126 |  | 
| 116 127 | 
             
                let(:invalid_params_hash) { {:d => nil} }
         | 
| @@ -118,32 +129,32 @@ module Requisite | |
| 118 129 | 
             
                it "attribute! raises an error if not found on model" do
         | 
| 119 130 | 
             
                  DummyApiModel.serialized_attributes { attribute! :d }
         | 
| 120 131 | 
             
                  response = DummyApiModel.new(invalid_params_hash)
         | 
| 121 | 
            -
                  proc { response.to_hash }.must_raise(NotImplementedError, "'d' not found on model")
         | 
| 132 | 
            +
                  _(proc { response.to_hash }).must_raise(NotImplementedError, "'d' not found on model")
         | 
| 122 133 | 
             
                end
         | 
| 123 134 |  | 
| 124 135 | 
             
                it 'attribute! does not raise an error if value on model is false' do
         | 
| 125 136 | 
             
                  params_hash = {:d => false}
         | 
| 126 137 | 
             
                  DummyApiModel.serialized_attributes { attribute! :d }
         | 
| 127 138 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 128 | 
            -
                  response.to_hash.must_equal({d: false})
         | 
| 139 | 
            +
                  _(response.to_hash).must_equal({d: false})
         | 
| 129 140 | 
             
                end
         | 
| 130 141 |  | 
| 131 142 | 
             
                it 'attribute! can be set to stringify fields' do
         | 
| 132 143 | 
             
                  DummyApiModel.serialized_attributes { attribute! :num, stringify: true }
         | 
| 133 144 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 134 | 
            -
                  response.to_hash.must_equal(:num => '12')
         | 
| 145 | 
            +
                  _(response.to_hash).must_equal(:num => '12')
         | 
| 135 146 | 
             
                end
         | 
| 136 147 |  | 
| 137 148 | 
             
                it 'attribute! can be set to rename fields' do
         | 
| 138 149 | 
             
                  DummyApiModel.serialized_attributes { attribute! :my_num, rename: :num }
         | 
| 139 150 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 140 | 
            -
                  response.to_hash.must_equal(:my_num => 12)
         | 
| 151 | 
            +
                  _(response.to_hash).must_equal(:my_num => 12)
         | 
| 141 152 | 
             
                end
         | 
| 142 153 |  | 
| 143 154 | 
             
                it 'sets the model from a hash' do
         | 
| 144 155 | 
             
                  DummyApiModel.serialized_attributes { }
         | 
| 145 156 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 146 | 
            -
                  response.model.must_equal(params_hash)
         | 
| 157 | 
            +
                  _(response.model).must_equal(params_hash)
         | 
| 147 158 | 
             
                end
         | 
| 148 159 |  | 
| 149 160 | 
             
                it 'sets the model from an object' do
         | 
| @@ -152,54 +163,54 @@ module Requisite | |
| 152 163 | 
             
                  mc.b = 2
         | 
| 153 164 | 
             
                  DummyApiModel.serialized_attributes { attribute :a }
         | 
| 154 165 | 
             
                  response = DummyApiModel.new(mc)
         | 
| 155 | 
            -
                  response.model.must_equal(mc)
         | 
| 156 | 
            -
                  response.to_hash.must_equal(:a => 'a')
         | 
| 166 | 
            +
                  _(response.model).must_equal(mc)
         | 
| 167 | 
            +
                  _(response.to_hash).must_equal(:a => 'a')
         | 
| 157 168 | 
             
                end
         | 
| 158 169 |  | 
| 159 170 | 
             
                it 'has alias a for attribute' do
         | 
| 160 171 | 
             
                  DummyApiModel.serialized_attributes { a :num }
         | 
| 161 172 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 162 | 
            -
                  response.to_hash.must_equal(:num => 12)
         | 
| 173 | 
            +
                  _(response.to_hash).must_equal(:num => 12)
         | 
| 163 174 | 
             
                end
         | 
| 164 175 |  | 
| 165 176 | 
             
                it 'has alias a! for attribute!' do
         | 
| 166 177 | 
             
                  DummyApiModel.serialized_attributes { a! :num }
         | 
| 167 178 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 168 | 
            -
                  response.to_hash.must_equal(:num => 12)
         | 
| 179 | 
            +
                  _(response.to_hash).must_equal(:num => 12)
         | 
| 169 180 | 
             
                end
         | 
| 170 181 |  | 
| 171 182 | 
             
                it 'can convert to json' do
         | 
| 172 183 | 
             
                  DummyApiModel.serialized_attributes { a! :num }
         | 
| 173 184 | 
             
                  response = DummyApiModel.new(params_hash)
         | 
| 174 | 
            -
                  response.to_json.must_equal("{\"num\":12}")
         | 
| 185 | 
            +
                  _(response.to_json).must_equal("{\"num\":12}")
         | 
| 175 186 | 
             
                end
         | 
| 176 187 |  | 
| 177 188 | 
             
                it 'drops non-listed parameters' do
         | 
| 178 189 | 
             
                  DummyApiModel.serialized_attributes { attribute :num }
         | 
| 179 190 | 
             
                  response = DummyApiModel.new({num: 12, other: 'value'})
         | 
| 180 | 
            -
                  response.to_hash.must_equal(:num => 12)
         | 
| 191 | 
            +
                  _(response.to_hash).must_equal(:num => 12)
         | 
| 181 192 | 
             
                end
         | 
| 182 193 |  | 
| 183 194 | 
             
                it 'works with ActionController::Parameters' do
         | 
| 184 195 | 
             
                  DummyApiModel.serialized_attributes { attribute :num }
         | 
| 185 196 | 
             
                  params = ActionController::Parameters.new(num: 12)
         | 
| 186 197 | 
             
                  response = DummyApiModel.new(params)
         | 
| 187 | 
            -
                  response.to_hash.must_equal(:num => 12)
         | 
| 198 | 
            +
                  _(response.to_hash).must_equal(:num => 12)
         | 
| 188 199 | 
             
                end
         | 
| 189 200 |  | 
| 190 201 | 
             
                describe 'with nested structures' do
         | 
| 191 202 |  | 
| 192 203 | 
             
                  describe 'with typed arrays' do
         | 
| 193 204 | 
             
                    it 'allows arrays of one type' do
         | 
| 194 | 
            -
                      DummyApiModel.serialized_attributes { attribute :ids, typed_array:  | 
| 205 | 
            +
                      DummyApiModel.serialized_attributes { attribute :ids, typed_array: Integer }
         | 
| 195 206 | 
             
                      response = DummyApiModel.new({ids: [1, 2, 3]})
         | 
| 196 | 
            -
                      response.to_hash.must_equal(:ids => [1, 2, 3])
         | 
| 207 | 
            +
                      _(response.to_hash).must_equal(:ids => [1, 2, 3])
         | 
| 197 208 | 
             
                    end
         | 
| 198 209 |  | 
| 199 210 | 
             
                    it 'raises errors when array has a wrongly typed value' do
         | 
| 200 211 | 
             
                      DummyApiModel.serialized_attributes { attribute :ids, typed_array: Requisite::Boolean }
         | 
| 201 212 | 
             
                      response = DummyApiModel.new({ids: [true, 'value', false]})
         | 
| 202 | 
            -
                      Proc.new {response.to_hash}.must_raise(BadTypeError)
         | 
| 213 | 
            +
                      _(Proc.new {response.to_hash}).must_raise(BadTypeError)
         | 
| 203 214 | 
             
                    end
         | 
| 204 215 | 
             
                  end
         | 
| 205 216 |  | 
| @@ -207,42 +218,42 @@ module Requisite | |
| 207 218 | 
             
                    it 'drops non listed parameters in nested hashes' do
         | 
| 208 219 | 
             
                      DummyApiModel.serialized_attributes { attribute :data, typed_hash: { num: Numeric, bool: Requisite::Boolean } }
         | 
| 209 220 | 
             
                      response = DummyApiModel.new({data: { num: 12, value: 'x', bool: true }})
         | 
| 210 | 
            -
                      response.to_hash.must_equal(:data => { :num => 12, :bool => true })
         | 
| 221 | 
            +
                      _(response.to_hash).must_equal(:data => { :num => 12, :bool => true })
         | 
| 211 222 | 
             
                    end
         | 
| 212 223 |  | 
| 213 224 | 
             
                    it 'can stringify nested hashes' do
         | 
| 214 225 | 
             
                      DummyApiModel.serialized_attributes { attribute :data, typed_hash: { num: Numeric }, stringify: true }
         | 
| 215 226 | 
             
                      response = DummyApiModel.new({data: { num: 12, value: 'x' }})
         | 
| 216 | 
            -
                      response.to_hash.must_equal(:data => "{:num=>12}")
         | 
| 227 | 
            +
                      _(response.to_hash).must_equal(:data => "{:num=>12}")
         | 
| 217 228 | 
             
                    end
         | 
| 218 229 |  | 
| 219 230 | 
             
                    it 'raises an error when nested hash values of the wrong type' do
         | 
| 220 231 | 
             
                      DummyApiModel.serialized_attributes { attribute :data, typed_hash: { num: Numeric } }
         | 
| 221 | 
            -
                      Proc.new {DummyApiModel.new({data: { num: '12'}}).to_hash}.must_raise(BadTypeError)
         | 
| 232 | 
            +
                      _(Proc.new {DummyApiModel.new({data: { num: '12'}}).to_hash}).must_raise(BadTypeError)
         | 
| 222 233 | 
             
                    end
         | 
| 223 234 |  | 
| 224 235 | 
             
                    it 'can rename param and work with nested hashes' do
         | 
| 225 236 | 
             
                      DummyApiModel.serialized_attributes { attribute :my_data, typed_hash: { num: Numeric }, rename: :data }
         | 
| 226 237 | 
             
                      response = DummyApiModel.new({data: { num: 12, value: 'x' }})
         | 
| 227 | 
            -
                      response.to_hash.must_equal(:my_data => { :num => 12 })
         | 
| 238 | 
            +
                      _(response.to_hash).must_equal(:my_data => { :num => 12 })
         | 
| 228 239 | 
             
                    end
         | 
| 229 240 |  | 
| 230 241 | 
             
                    it 'can set a default value for a nested hash' do
         | 
| 231 242 | 
             
                      DummyApiModel.serialized_attributes { attribute :data, typed_hash: { num: Numeric }, default: { num: 4 } }
         | 
| 232 243 | 
             
                      response = DummyApiModel.new({data: { value: 'x' }})
         | 
| 233 | 
            -
                      response.to_hash.must_equal(:data => { :num => 4 })
         | 
| 244 | 
            +
                      _(response.to_hash).must_equal(:data => { :num => 4 })
         | 
| 234 245 | 
             
                    end
         | 
| 235 246 |  | 
| 236 247 | 
             
                    it 'drops non listed fields with attribute!' do
         | 
| 237 248 | 
             
                      DummyApiModel.serialized_attributes { attribute! :data, typed_hash: { num: Numeric } }
         | 
| 238 249 | 
             
                      response = DummyApiModel.new({data: { num: 12, value: 'x' }})
         | 
| 239 | 
            -
                      response.to_hash.must_equal(:data => { :num => 12 })
         | 
| 250 | 
            +
                      _(response.to_hash).must_equal(:data => { :num => 12 })
         | 
| 240 251 | 
             
                    end
         | 
| 241 252 |  | 
| 242 253 | 
             
                    it 'attribute! does not raise an error with missing values in hash' do
         | 
| 243 254 | 
             
                      DummyApiModel.serialized_attributes { attribute! :data, typed_hash: { num: Numeric } }
         | 
| 244 255 | 
             
                      response = DummyApiModel.new({data: { value: 'x' }})
         | 
| 245 | 
            -
                      response.to_hash.must_equal(:data => { })
         | 
| 256 | 
            +
                      _(response.to_hash).must_equal(:data => { })
         | 
| 246 257 | 
             
                    end
         | 
| 247 258 | 
             
                  end
         | 
| 248 259 |  | 
| @@ -250,19 +261,19 @@ module Requisite | |
| 250 261 | 
             
                    it 'should parse scalar hashes permitting anything scalar' do
         | 
| 251 262 | 
             
                      DummyApiModel.serialized_attributes { attribute :data, scalar_hash: true }
         | 
| 252 263 | 
             
                      response = DummyApiModel.new({data: { num: 12, value: 'x', :truthy => false }})
         | 
| 253 | 
            -
                      response.to_hash.must_equal(:data => { :num => 12, :value => 'x', :truthy => false })
         | 
| 264 | 
            +
                      _(response.to_hash).must_equal(:data => { :num => 12, :value => 'x', :truthy => false })
         | 
| 254 265 | 
             
                    end
         | 
| 255 266 |  | 
| 256 267 | 
             
                    it 'should parse a renamed scalar hash' do
         | 
| 257 268 | 
             
                      DummyApiModel.serialized_attributes { attribute :my_data, scalar_hash: true, rename: :data }
         | 
| 258 269 | 
             
                      response = DummyApiModel.new({data: { num: 12, value: 'x' }})
         | 
| 259 | 
            -
                      response.to_hash.must_equal(:my_data => { :num => 12, :value => 'x' })
         | 
| 270 | 
            +
                      _(response.to_hash).must_equal(:my_data => { :num => 12, :value => 'x' })
         | 
| 260 271 | 
             
                    end
         | 
| 261 272 |  | 
| 262 273 | 
             
                    it 'should stringify a scalar hash' do
         | 
| 263 274 | 
             
                      DummyApiModel.serialized_attributes { attribute :data, scalar_hash: true, stringify: true }
         | 
| 264 275 | 
             
                      response = DummyApiModel.new({data: { num: 12, value: 'x' }})
         | 
| 265 | 
            -
                      response.to_hash.must_equal(:data => "{:num=>12, :value=>\"x\"}")
         | 
| 276 | 
            +
                      _(response.to_hash).must_equal(:data => "{:num=>12, :value=>\"x\"}")
         | 
| 266 277 | 
             
                    end
         | 
| 267 278 |  | 
| 268 279 | 
             
                    it 'should parse scalar hashes permitting anything scalar with object' do
         | 
| @@ -271,13 +282,13 @@ module Requisite | |
| 271 282 | 
             
                      mc.b = { num: 12, value: 'x' }
         | 
| 272 283 | 
             
                      DummyApiModel.serialized_attributes { attribute :b, scalar_hash: true }
         | 
| 273 284 | 
             
                      response = DummyApiModel.new(mc)
         | 
| 274 | 
            -
                      response.to_hash.must_equal(:b => { :num => 12, :value => 'x' })
         | 
| 285 | 
            +
                      _(response.to_hash).must_equal(:b => { :num => 12, :value => 'x' })
         | 
| 275 286 | 
             
                    end
         | 
| 276 287 |  | 
| 277 288 | 
             
                    it 'should fail to parse scalar hashes when non scalar values present' do
         | 
| 278 289 | 
             
                      DummyApiModel.serialized_attributes { attribute :data, scalar_hash: true }
         | 
| 279 | 
            -
                      Proc.new { DummyApiModel.new({data: { num: 12, value: { nested: 'value' } }}).to_hash}.must_raise(BadTypeError)
         | 
| 280 | 
            -
                      Proc.new { DummyApiModel.new({data: { num: 12, value: ['array value'] }}).to_hash}.must_raise(BadTypeError)
         | 
| 290 | 
            +
                      _(Proc.new { DummyApiModel.new({data: { num: 12, value: { nested: 'value' } }}).to_hash}).must_raise(BadTypeError)
         | 
| 291 | 
            +
                      _(Proc.new { DummyApiModel.new({data: { num: 12, value: ['array value'] }}).to_hash}).must_raise(BadTypeError)
         | 
| 281 292 | 
             
                    end
         | 
| 282 293 |  | 
| 283 294 | 
             
                    it 'should fail to parse scalar hashes permitting anything scalar with object' do
         | 
| @@ -286,25 +297,25 @@ module Requisite | |
| 286 297 | 
             
                      mc.b = { value: { nested: 'value' } }
         | 
| 287 298 | 
             
                      DummyApiModel.serialized_attributes { attribute :b, scalar_hash: true }
         | 
| 288 299 | 
             
                      response = DummyApiModel.new(mc)
         | 
| 289 | 
            -
                      Proc.new { response.to_hash }.must_raise(BadTypeError)
         | 
| 300 | 
            +
                      _(Proc.new { response.to_hash }).must_raise(BadTypeError)
         | 
| 290 301 | 
             
                    end
         | 
| 291 302 |  | 
| 292 303 | 
             
                    it 'can set a default value for a scalar hash' do
         | 
| 293 304 | 
             
                      DummyApiModel.serialized_attributes { attribute :data, scalar_hash: true, default: { num: 9, value: 'y' } }
         | 
| 294 305 | 
             
                      response = DummyApiModel.new({data: { }})
         | 
| 295 | 
            -
                      response.to_hash.must_equal(:data => { :num => 9, :value => 'y' })
         | 
| 306 | 
            +
                      _(response.to_hash).must_equal(:data => { :num => 9, :value => 'y' })
         | 
| 296 307 | 
             
                    end
         | 
| 297 308 |  | 
| 298 309 | 
             
                    it 'doesnt raise with attribute! when an empty hash passed' do
         | 
| 299 310 | 
             
                      DummyApiModel.serialized_attributes { attribute! :data, scalar_hash: true }
         | 
| 300 311 | 
             
                      response = DummyApiModel.new({data: {}})
         | 
| 301 | 
            -
                      response.to_hash.must_equal(:data => {})
         | 
| 312 | 
            +
                      _(response.to_hash).must_equal(:data => {})
         | 
| 302 313 | 
             
                    end
         | 
| 303 314 |  | 
| 304 315 | 
             
                    it 'raises with attribute! when nil is passed' do
         | 
| 305 316 | 
             
                      DummyApiModel.serialized_attributes { attribute! :data, scalar_hash: true }
         | 
| 306 317 | 
             
                      response = DummyApiModel.new({data: nil})
         | 
| 307 | 
            -
                      Proc.new {response.to_hash}.must_raise(NotImplementedError)
         | 
| 318 | 
            +
                      _(Proc.new {response.to_hash}).must_raise(NotImplementedError)
         | 
| 308 319 | 
             
                    end
         | 
| 309 320 | 
             
                  end
         | 
| 310 321 | 
             
                end
         | 
| @@ -1,15 +1,17 @@ | |
| 1 1 | 
             
            require 'test_helper'
         | 
| 2 | 
            +
            require 'benchmark'
         | 
| 2 3 |  | 
| 3 4 | 
             
            # Example Object
         | 
| 4 5 | 
             
            class ApiUser < Requisite::ApiModel
         | 
| 6 | 
            +
             | 
| 5 7 | 
             
              serialized_attributes do
         | 
| 6 8 | 
             
                attribute :id, type: String
         | 
| 7 9 | 
             
                attribute :user_id
         | 
| 8 10 | 
             
                attribute :email, type: String
         | 
| 9 11 | 
             
                attribute :name, type: String
         | 
| 10 | 
            -
                attribute :created_at, type:  | 
| 12 | 
            +
                attribute :created_at, type: Integer
         | 
| 11 13 | 
             
                attribute :last_seen_user_agent, type: String
         | 
| 12 | 
            -
                attribute :last_request_at, type:  | 
| 14 | 
            +
                attribute :last_request_at, type: Integer
         | 
| 13 15 | 
             
                attribute :unsubscribed_from_emails, type: Requisite::Boolean
         | 
| 14 16 | 
             
                attribute :update_last_request_at, type: Requisite::Boolean
         | 
| 15 17 | 
             
                attribute :new_session, type: Requisite::Boolean
         | 
| @@ -17,7 +19,7 @@ class ApiUser < Requisite::ApiModel | |
| 17 19 | 
             
                attribute :company
         | 
| 18 20 | 
             
                attribute :companies
         | 
| 19 21 | 
             
              end
         | 
| 20 | 
            -
             | 
| 22 | 
            +
             | 
| 21 23 | 
             
              # Ensure that at least one identifier is passed
         | 
| 22 24 | 
             
              def preprocess_model
         | 
| 23 25 | 
             
                identifier = attribute_from_model(:id)
         | 
| @@ -25,10 +27,30 @@ class ApiUser < Requisite::ApiModel | |
| 25 27 | 
             
                identifier ||= attribute_from_model(:email)
         | 
| 26 28 | 
             
                raise StandardError unless identifier
         | 
| 27 29 | 
             
              end
         | 
| 28 | 
            -
             | 
| 30 | 
            +
             | 
| 31 | 
            +
              def last_attribute_fetch_time
         | 
| 32 | 
            +
                @last_attribute_fetch_time
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              def attribute_names
         | 
| 36 | 
            +
                @attribute_names
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              def around_each_attribute(name)
         | 
| 40 | 
            +
                @last_attribute_fetch_time = nil
         | 
| 41 | 
            +
                @attribute_names ||= []
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                result = nil
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                @last_attribute_fetch_time = Benchmark.measure do
         | 
| 46 | 
            +
                  result = yield
         | 
| 47 | 
            +
                end.total
         | 
| 48 | 
            +
                @attribute_names << name
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 29 51 | 
             
              # We want to accept someone sending `created_at` or `created` as parameters
         | 
| 30 52 | 
             
              def created_at
         | 
| 31 | 
            -
                with_type!( | 
| 53 | 
            +
                with_type!(Integer) { attribute_from_model(:created_at) || attribute_from_model(:created) }
         | 
| 32 54 | 
             
              end
         | 
| 33 55 | 
             
            end
         | 
| 34 56 |  | 
| @@ -71,13 +93,13 @@ module Requisite | |
| 71 93 | 
             
                  })
         | 
| 72 94 | 
             
                  user.name.must_equal('Bob')
         | 
| 73 95 | 
             
                end
         | 
| 74 | 
            -
             | 
| 96 | 
            +
             | 
| 75 97 | 
             
                it 'raises an error without an identifier' do
         | 
| 76 98 | 
             
                  user_request_params = { :name => 'Bob' }
         | 
| 77 99 | 
             
                  user = ApiUser.new(user_request_params)
         | 
| 78 100 | 
             
                  proc { user.to_hash }.must_raise(StandardError)
         | 
| 79 101 | 
             
                end
         | 
| 80 | 
            -
             | 
| 102 | 
            +
             | 
| 81 103 | 
             
                it 'raises an error when created or created_at is not of the right type' do
         | 
| 82 104 | 
             
                  user_request_params = { :user_id => 'abcdef', :created => 'Thursday' }
         | 
| 83 105 | 
             
                  user = ApiUser.new(user_request_params)
         | 
| @@ -116,7 +138,7 @@ module Requisite | |
| 116 138 | 
             
                    :new_attribute => 'hi'
         | 
| 117 139 | 
             
                  })
         | 
| 118 140 | 
             
                end
         | 
| 119 | 
            -
             | 
| 141 | 
            +
             | 
| 120 142 | 
             
                it 'accepts a user model' do
         | 
| 121 143 | 
             
                  user_model = UserModel.new
         | 
| 122 144 | 
             
                  user_model.user_id = 'abcdef'
         | 
| @@ -129,7 +151,7 @@ module Requisite | |
| 129 151 | 
             
                  })
         | 
| 130 152 | 
             
                  user.name.must_equal('Bob')
         | 
| 131 153 | 
             
                end
         | 
| 132 | 
            -
             | 
| 154 | 
            +
             | 
| 133 155 | 
             
                it 'accepts a user model and renders nils if asked' do
         | 
| 134 156 | 
             
                  user_model = UserModel.new
         | 
| 135 157 | 
             
                  user_model.user_id = 'abcdef'
         | 
| @@ -152,5 +174,16 @@ module Requisite | |
| 152 174 | 
             
                  })
         | 
| 153 175 | 
             
                  user.name.must_equal('Bob')
         | 
| 154 176 | 
             
                end
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                it 'calls around_each_attribute for each attribute' do
         | 
| 179 | 
            +
                  user_model = UserModel.new
         | 
| 180 | 
            +
                  user_model.user_id = 'abcdef'
         | 
| 181 | 
            +
                  user = ApiUser.new(user_model)
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                  user.to_hash(show_nil: true)
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                  user.attribute_names.must_equal [:id, :user_id, :email, :name, :last_seen_user_agent, :last_request_at, :unsubscribed_from_emails, :update_last_request_at, :new_session, :custom_data, :company, :companies]
         | 
| 186 | 
            +
                  user.last_attribute_fetch_time.must_be :>, 0
         | 
| 187 | 
            +
                end
         | 
| 155 188 | 
             
              end
         | 
| 156 189 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: requisite
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.4. | 
| 4 | 
            +
              version: 0.4.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - James Osler
         | 
| 8 | 
            -
            autorequire: | 
| 8 | 
            +
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2025-03-25 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: minitest
         | 
| @@ -82,7 +82,7 @@ homepage: https://www.intercom.io | |
| 82 82 | 
             
            licenses:
         | 
| 83 83 | 
             
            - Apache License Version 2.0
         | 
| 84 84 | 
             
            metadata: {}
         | 
| 85 | 
            -
            post_install_message: | 
| 85 | 
            +
            post_install_message:
         | 
| 86 86 | 
             
            rdoc_options: []
         | 
| 87 87 | 
             
            require_paths:
         | 
| 88 88 | 
             
            - lib
         | 
| @@ -97,9 +97,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 97 97 | 
             
                - !ruby/object:Gem::Version
         | 
| 98 98 | 
             
                  version: '0'
         | 
| 99 99 | 
             
            requirements: []
         | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
            signing_key: 
         | 
| 100 | 
            +
            rubygems_version: 3.5.22
         | 
| 101 | 
            +
            signing_key:
         | 
| 103 102 | 
             
            specification_version: 4
         | 
| 104 103 | 
             
            summary: Strongly defined models for HTTP APIs
         | 
| 105 104 | 
             
            test_files:
         |