hashme 0.2.6 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6f50c8f90a959db106926caadbdaee800ed55073
4
- data.tar.gz: 422d08c967c35172445489d6716b1a799f53b299
2
+ SHA256:
3
+ metadata.gz: 157002395c66c0c5a0d4ffe7c13cf16f93a5ee016a8c42c9388362b7f80b65ae
4
+ data.tar.gz: '032850cb9db195516320a53afc10432d918b0f066fc4d0ed0cf7291216a336f5'
5
5
  SHA512:
6
- metadata.gz: 417b46455a892130eb7e4b8781dccfa7530ac10cf93d656863c78d3ebc5b59a2f81b8378acc9736944bd25062f37c65eb475ce8522b8af961b888802300318dd
7
- data.tar.gz: 0c8c5cd1b21333070e76c6e74f9c40095c4f48117086a72be98f61db7a0201f79cfb83ac5c3468e8ebaf6bf5d52e093bc5bb88671af8a08c737bd72b8feba15b
6
+ metadata.gz: 1c6820691be9c3b71e0e570d3bb4a37315e896e3203096017905750f9c18aa122039bc7a889dc67a1ff24ae2af7da2956803de806490e7224e3b40bda2de0f6c
7
+ data.tar.gz: 8e397827a0eb98c019e88b2a4d6155817693d50be1e90c33f6ff8a295dbd980198c24887daae41d68a3965fa50e96a987fa125e379c7503b8d0878c86da3b1fd
data/README.md CHANGED
@@ -120,6 +120,13 @@ u.errors.first # [:email, "can't be blank"]
120
120
 
121
121
  ## History
122
122
 
123
+ ### 0.3.0 - 2023-01-23
124
+
125
+ * Making `CastArray` provide the whole `Array` API by inherityin from it
126
+ * Removing default value fallback to fix bug with booleans
127
+ * Adding support for `URI` objects typecasting
128
+ * Adding `Boolean` as an alias of `TrueClass`
129
+
123
130
  ### 0.2.6 - 2017-09-12
124
131
 
125
132
  * Setting ActiveModel minimum to 4.0, adding travis config to test both 4.0 and 5.0. (@samlown)
@@ -158,7 +165,7 @@ u.errors.first # [:email, "can't be blank"]
158
165
  * Refactoring to use `class_attribute` for properties hash for improved inheritance.
159
166
 
160
167
  ### 0.1.1 - 2014-01-21
161
-
168
+
162
169
  * Fixing dependency on ActiveModel >= 3.0
163
170
 
164
171
  ### 0.1.0 - 2014-01-15
data/hashme.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency "activemodel", ">= 4.0"
22
22
 
23
- spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "bundler"
24
24
  spec.add_development_dependency "rake"
25
25
  spec.add_development_dependency "rspec"
26
26
  end
@@ -1,7 +1,7 @@
1
1
  module Hashme
2
2
  module Attributes
3
3
  include Enumerable
4
-
4
+
5
5
  extend Forwardable
6
6
 
7
7
  def_delegators :_attributes, :to_a,
@@ -2,60 +2,83 @@ require 'forwardable'
2
2
 
3
3
  module Hashme
4
4
 
5
- # The Hashme CastedArray is a special object wrapper that allows other Model's
6
- # or Objects to be stored in an array, but maintain casted ownership.
7
- #
8
- # Adding objects will automatically assign the Array's owner, as opposed
9
- # to the array itself.
10
- #
11
- class CastedArray
12
- extend Forwardable
13
-
5
+ # The Hashme CastedArray is a special Array that typecasts each item according to a given property
6
+ class CastedArray < Array
14
7
  attr_reader :property
15
8
 
16
- def_delegators :@_array,
17
- :to_a, :==, :eql?, :size,
18
- :first, :last, :at, :length,
19
- :each, :reject, :empty?, :map, :collect,
20
- :clear, :pop, :shift, :delete, :delete_at,
21
- :encode_json, :as_json, :to_json,
22
- :inspect, :any?
23
-
24
- def initialize(property, owner, values = [])
25
- @_array = []
9
+ def initialize(property, vals = [])
26
10
  @property = property
27
- if values.respond_to?(:each)
28
- values.each do |value|
29
- self.push(value)
30
- end
31
- end
11
+ super build_all(vals)
12
+ end
13
+
14
+ def <<(val)
15
+ super build(val)
16
+ end
17
+
18
+ def push(*vals)
19
+ super *build_all(vals)
20
+ end
21
+
22
+ alias append push
23
+
24
+ def concat(*arrays)
25
+ super *arrays.map { |array| build_all(array) }
32
26
  end
33
27
 
34
- def <<(obj)
35
- @_array << instantiate_and_build(obj)
28
+ def insert(index, *vals)
29
+ super index, *build_all(vals)
36
30
  end
37
31
 
38
- def push(obj)
39
- @_array.push(instantiate_and_build(obj))
32
+ def unshift(*vals)
33
+ super *build_all(vals)
40
34
  end
41
35
 
42
- def unshift(obj)
43
- @_array.unshift(instantiate_and_build(obj))
36
+ alias prepend unshift
37
+
38
+ def replace(array)
39
+ super build_all(array)
44
40
  end
45
-
46
- def [] index
47
- @_array[index]
41
+
42
+ def []=(*args)
43
+ args = args.dup
44
+ args[-1] = build(args[-1])
45
+ super *args
48
46
  end
49
47
 
50
- def []= index, obj
51
- @_array[index] = instantiate_and_build(obj)
48
+ def fill(*args, &block)
49
+ if block
50
+ super *args do |index|
51
+ val = block.call(index)
52
+ build(val)
53
+ end
54
+ else
55
+ args = args.dup
56
+ args[0] = build(args[0])
57
+ super *args
58
+ end
52
59
  end
53
-
60
+
61
+ def collect!(&block)
62
+ if block
63
+ super do |element|
64
+ val = block.call(element)
65
+ build(val)
66
+ end
67
+ else
68
+ super
69
+ end
70
+ end
71
+
72
+ alias map! collect!
73
+
54
74
  protected
55
75
 
56
- def instantiate_and_build(obj)
57
- property.build(self, obj)
76
+ def build_all(vals)
77
+ vals.map { |val| build(val) }
58
78
  end
59
79
 
80
+ def build(val)
81
+ property.build(val)
82
+ end
60
83
  end
61
84
  end
@@ -14,7 +14,7 @@ module Hashme
14
14
  def set_attribute(name, value)
15
15
  property = get_property(name)
16
16
  if property
17
- value = property.build(self, value)
17
+ value = property.build(value)
18
18
  if value.nil?
19
19
  delete(property.name)
20
20
  else
@@ -45,9 +45,9 @@ module Hashme
45
45
  set_attribute(key, value)
46
46
  end
47
47
  end
48
-
48
+
49
49
  private
50
-
50
+
51
51
  def get_property(name)
52
52
  self.class.properties[name.to_sym]
53
53
  end
@@ -68,7 +68,7 @@ module Hashme
68
68
  def define_property_methods(property)
69
69
  # Getter
70
70
  define_method(property.name) do
71
- get_attribute(property.name) || property.default
71
+ get_attribute(property.name)
72
72
  end
73
73
  # Setter
74
74
  define_method "#{property.name}=" do |value|
@@ -33,11 +33,11 @@ module Hashme
33
33
  end
34
34
 
35
35
  # Build a new object of the type defined by the property.
36
- def build(owner, value)
36
+ def build(value)
37
37
  if array && value.is_a?(Array)
38
- CastedArray.new(self, owner, value)
38
+ CastedArray.new(self, value)
39
39
  else
40
- PropertyCasting.cast(self, owner, value)
40
+ PropertyCasting.cast(self, value)
41
41
  end
42
42
  end
43
43
 
@@ -1,5 +1,8 @@
1
1
  module Hashme
2
2
 
3
+ # Alias TrueClass as Boolean, so that it can be used as property types
4
+ Boolean = TrueClass
5
+
3
6
  # Special property casting for reveiving data from sources without Ruby types, such as query
4
7
  # parameters from an API or JSON documents.
5
8
  #
@@ -7,10 +10,10 @@ module Hashme
7
10
  module PropertyCasting
8
11
  extend self
9
12
 
10
- CASTABLE_TYPES = [String, Symbol, TrueClass, Integer, Float, BigDecimal, DateTime, Time, Date, Class]
13
+ CASTABLE_TYPES = [String, Symbol, TrueClass, Integer, Float, BigDecimal, DateTime, Time, Date, Class, URI]
11
14
 
12
15
  # Automatically typecast the provided value into an instance of the provided type.
13
- def cast(property, owner, value)
16
+ def cast(property, value)
14
17
  return nil if value.nil?
15
18
  type = property.type
16
19
  if value.instance_of?(type) || type == Object
@@ -186,5 +189,12 @@ module Hashme
186
189
  nil
187
190
  end
188
191
 
192
+ # Typecast a value to URI
193
+ def typecast_to_uri(value)
194
+ URI(value)
195
+ rescue ArgumentError
196
+ nil
197
+ end
198
+
189
199
  end
190
200
  end
@@ -1,7 +1,7 @@
1
1
  module Hashme
2
2
  module Validations
3
3
  class CastedAttributeValidator < ActiveModel::EachValidator
4
-
4
+
5
5
  def validate_each(document, attribute, value)
6
6
  is_array = value.is_a?(Array) || value.is_a?(CastedArray)
7
7
  values = is_array ? value : [value]
@@ -1,3 +1,3 @@
1
1
  module Hashme
2
- VERSION = "0.2.6"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/hashme.rb CHANGED
@@ -2,6 +2,8 @@ require "hashme/version"
2
2
 
3
3
  # External dependencies
4
4
 
5
+ require "uri"
6
+
5
7
  require "active_model"
6
8
  require "active_model/naming"
7
9
  require "active_model/serialization"
@@ -1,14 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Hashme do
4
-
4
+
5
5
  before :each do
6
6
  @model = Class.new do
7
7
  include Hashme
8
8
  property :name, String
9
9
  end
10
10
  end
11
-
11
+
12
12
  describe '.build' do
13
13
  it "should create a Model and give a block to build it" do
14
14
  expect(@model).to receive(:call_in_block)
@@ -1,11 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Hashme::CastedArray do
4
-
5
- let :owner do
6
- double()
7
- end
8
-
9
4
  let :submodel do
10
5
  Class.new do
11
6
  include Hashme
@@ -13,43 +8,124 @@ describe Hashme::CastedArray do
13
8
  end
14
9
  end
15
10
 
16
- describe "#initialize" do
17
- let :property do
18
- Hashme::Property.new(:name, String)
19
- end
20
- before :each do
21
- @obj = Hashme::CastedArray.new(property, owner, ['test'])
22
- end
11
+ let :property do
12
+ Hashme::Property.new(:item, submodel)
13
+ end
14
+
15
+ subject do
16
+ Hashme::CastedArray.new(property, [{:name => 'test'}])
17
+ end
23
18
 
19
+ describe "#initialize" do
24
20
  it "should prepare array" do
25
- expect(@obj.length).to eql(1)
21
+ expect(subject.length).to eql(1)
26
22
  end
27
23
 
28
24
  it "should set property" do
29
- expect(@obj.property).to eql(property)
25
+ expect(subject.property).to eql(property)
30
26
  end
31
27
 
32
28
  it "should instantiate and cast each value" do
33
- expect(@obj.first).to eql("test")
34
- expect(@obj.first.class).to eql(String)
29
+ expect(subject.first.class).to eql(submodel)
30
+ expect(subject.first.name).to eql('test')
35
31
  end
36
32
  end
37
33
 
38
- describe "adding to array" do
39
- subject do
40
- Hashme::CastedArray.new(property, owner, [{:name => 'test'}])
41
- end
42
- let :property do
43
- Hashme::Property.new(:item, submodel)
44
- end
34
+ it "should cast items added to the array" do
35
+ subject << {:name => 'test2'}
36
+ expect(subject.last.class).to eql(submodel)
37
+ expect(subject.last.name).to eql('test2')
38
+ end
45
39
 
46
- it "should cast new items" do
47
- subject << {:name => 'test2'}
48
- expect(subject.last.class).to eql(submodel)
49
- expect(subject.first.name).to eql('test')
50
- expect(subject.last.name).to eql('test2')
51
- end
40
+ it "should cast items using `push`" do
41
+ subject.push({ :name => 'test2'}, { :name => 'test3' })
42
+ expect(subject.last.class).to eql(submodel)
43
+ expect(subject.last.name).to eql('test3')
44
+ end
45
+
46
+ it "should cast items using `concat`" do
47
+ subject.concat([{ :name => 'test2'}], [{ :name => 'test3' }])
48
+ expect(subject.last.class).to eql(submodel)
49
+ expect(subject.last.name).to eql('test3')
50
+ end
51
+
52
+ it "should cast items using `insert`" do
53
+ subject.insert(0, { :name => 'test2'}, { :name => 'test3' })
54
+ expect(subject[1].class).to eql(submodel)
55
+ expect(subject[1].name).to eql('test3')
56
+ end
57
+
58
+ it "should cast items using `unshift`" do
59
+ subject.unshift({ :name => 'test2'}, { :name => 'test3' })
60
+ expect(subject[1].class).to eql(submodel)
61
+ expect(subject[1].name).to eql('test3')
62
+ end
63
+
64
+ it "should cast items using `replace`" do
65
+ subject.replace([{ :name => 'test2'}, { :name => 'test3' }])
66
+ expect(subject[1].class).to eql(submodel)
67
+ expect(subject[1].name).to eql('test3')
68
+ end
69
+
70
+ it "should cast items using `[]=`" do
71
+ subject[5, 10] = { :name => 'test2'}
72
+ expect(subject[5].class).to eql(submodel)
73
+ expect(subject[5].name).to eql('test2')
74
+ end
75
+
76
+ it "should cast items using fill with block" do
77
+ subject.fill(0) { |index| { :name => "test#{index}" } }
78
+ expect(subject.last.class).to eql(submodel)
79
+ expect(subject.last.name).to eql('test0')
80
+ end
81
+
82
+ it "should cast items using fill without block" do
83
+ subject.fill({ :name => "test-filled" }, 0)
84
+ expect(subject.last.class).to eql(submodel)
85
+ expect(subject.last.name).to eql('test-filled')
86
+ end
87
+
88
+ it "should cast items using `collect!` with a block" do
89
+ subject.collect! { |item| { :name => "Mapped #{item.name}" } }
90
+ expect(subject.first.name).to eql('Mapped test')
91
+ end
52
92
 
93
+ it "should support Enumerable methods" do
94
+ map = subject.map(&:name)
95
+ expect(map).to be_an(Enumerable)
96
+ expect(map.first).to eql('test')
53
97
  end
54
98
 
99
+ it "should compare for equality with CastedArrays" do
100
+ same = Hashme::CastedArray.new(property, [{:name => 'test'}])
101
+ different = Hashme::CastedArray.new(property, [{:name => 'test2'}])
102
+
103
+ expect(subject == same).to be(true)
104
+ expect(subject.eql?(same)).to be(true)
105
+
106
+ expect(subject == different).to be(false)
107
+ expect(subject.eql?(different)).to be(false)
108
+ end
109
+
110
+ it "should compare for equality with Arrays" do
111
+ same = [submodel.new(:name => 'test')]
112
+ different = [submodel.new(:name => 'test2')]
113
+
114
+ expect(subject == same).to be(true)
115
+ expect(subject.eql?(same)).to be(true)
116
+
117
+ expect(subject == different).to be(false)
118
+ expect(subject.eql?(different)).to be(false)
119
+ end
120
+
121
+ it "should be compared for equality with Arrays" do
122
+ same = [submodel.new(:name => 'test')]
123
+ different = [submodel.new(:name => 'test2')]
124
+
125
+ expect(same == subject).to be(true)
126
+ expect(same.eql?(subject)).to be(true)
127
+
128
+ expect(different == same).to be(false)
129
+ expect(different.eql?(same)).to be(false)
130
+ end
55
131
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Hashme::Properties do
4
-
4
+
5
5
  before :all do
6
6
  @aux_model = Class.new do
7
7
  include Hashme
@@ -39,7 +39,7 @@ describe Hashme::Properties do
39
39
  it "should set and cast attribute with property" do
40
40
  property = model.send(:properties)[:name]
41
41
  name = "Fred Flinstone"
42
- expect(property).to receive(:build).with(obj, name).and_return(name)
42
+ expect(property).to receive(:build).with(name).and_return(name)
43
43
  obj.set_attribute(:name, name)
44
44
  expect(obj[:name]).to eql(name)
45
45
  end
@@ -111,12 +111,18 @@ describe Hashme::Properties do
111
111
  obj.desc = "test"
112
112
  expect(obj.desc).to eql("test")
113
113
  end
114
-
114
+
115
115
  it "should return nil on property with no default" do
116
116
  model.property :nickname, String
117
117
  expect(obj.nickname).to be_nil
118
118
  end
119
119
 
120
+ it 'should not return the default value when a Boolean is set to false' do
121
+ model.property :flag, TrueClass, :default => true
122
+ obj.flag = false
123
+ expect(obj.flag).to be(false)
124
+ end
125
+
120
126
  it "should create helper method with support for default values" do
121
127
  model.property :name, String, :default => "Sam"
122
128
  expect(obj.name).to eql("Sam")
@@ -12,10 +12,11 @@ class Course
12
12
  property :started_on, Date
13
13
  property :updated_at, DateTime
14
14
  property :active, TrueClass
15
- property :very_active, TrueClass
15
+ property :very_active, Boolean
16
16
  property :klass, Class
17
17
  property :currency, String, :default => 'EUR'
18
18
  property :symbol, Symbol
19
+ property :uri, URI
19
20
  end
20
21
 
21
22
  describe Hashme::PropertyCasting do
@@ -598,19 +599,19 @@ describe Hashme::PropertyCasting do
598
599
  end
599
600
  end
600
601
 
601
- describe 'when type primitive is a Boolean' do
602
+ describe 'when type primitive is a TrueClass' do
602
603
 
603
604
  [ true, 'true', 'TRUE', '1', 1, 't', 'T' ].each do |value|
604
605
  it "returns true when value is #{value.inspect}" do
605
606
  course.active = value
606
- expect(course['active']).to be_truthy
607
+ expect(course['active']).to be(true)
607
608
  end
608
609
  end
609
610
 
610
611
  [ false, 'false', 'FALSE', '0', 0, 'f', 'F' ].each do |value|
611
612
  it "returns false when value is #{value.inspect}" do
612
613
  course.active = value
613
- expect(course['active']).to be_falsey
614
+ expect(course['active']).to be(false)
614
615
  end
615
616
  end
616
617
 
@@ -620,5 +621,29 @@ describe Hashme::PropertyCasting do
620
621
  expect(course['active']).to be_nil
621
622
  end
622
623
  end
624
+
625
+ it "supports Boolean as an alias of TrueClass" do
626
+ course.very_active = 't'
627
+ expect(course['very_active']).to be(true)
628
+ end
629
+
630
+ end
631
+
632
+ describe 'when type primitive is an URI' do
633
+ it 'returns same value if an uri' do
634
+ value = URI('https://invopop.com')
635
+ course.uri = value
636
+ expect(course['uri']).to equal(value)
637
+ end
638
+
639
+ it 'returns an URI if parses as one' do
640
+ course.uri = 'https://invopop.com'
641
+ expect(course['uri']).to eql(URI('https://invopop.com'))
642
+ end
643
+
644
+ it 'does not typecast non-uri values' do
645
+ course.uri = 1234
646
+ expect(course['uri']).to be_nil
647
+ end
623
648
  end
624
649
  end
@@ -6,10 +6,6 @@ describe Hashme::Property do
6
6
  described_class
7
7
  end
8
8
 
9
- let :owner do
10
- double()
11
- end
12
-
13
9
  let :submodel do
14
10
  Class.new do
15
11
  include Hashme
@@ -61,7 +57,7 @@ describe Hashme::Property do
61
57
  context "without an array" do
62
58
  it "should build a new object" do
63
59
  prop = subject.new(:date, Time)
64
- obj = prop.build(owner, "2013-06-02T12:00:00Z")
60
+ obj = prop.build("2013-06-02T12:00:00Z")
65
61
  expect(obj.class).to eql(Time)
66
62
  expect(obj).to eql(Time.utc(2013, 6, 2, 12, 0, 0))
67
63
  end
@@ -70,13 +66,13 @@ describe Hashme::Property do
70
66
  context "with an array" do
71
67
  it "should convert regular array to casted array" do
72
68
  prop = subject.new(:dates, [Time])
73
- obj = prop.build(owner, ["2013-06-02T12:00:00"])
69
+ obj = prop.build(["2013-06-02T12:00:00"])
74
70
  expect(obj.class).to eql(Hashme::CastedArray)
75
71
  expect(obj.first.class).to eql(Time)
76
72
  end
77
73
  it "should handle complex objects" do
78
74
  prop = subject.new(:items, [submodel])
79
- obj = prop.build(owner, [{:name => 'test'}])
75
+ obj = prop.build([{:name => 'test'}])
80
76
  expect(obj.class).to eql(Hashme::CastedArray)
81
77
  expect(obj.first.class).to eql(submodel)
82
78
  expect(obj.first.name).to eql('test')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashme
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Lown
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-12 00:00:00.000000000 Z
11
+ date: 2023-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -28,16 +28,16 @@ dependencies:
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '1.3'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '1.3'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -119,8 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
119
  - !ruby/object:Gem::Version
120
120
  version: '0'
121
121
  requirements: []
122
- rubyforge_project:
123
- rubygems_version: 2.6.8
122
+ rubygems_version: 3.4.1
124
123
  signing_key:
125
124
  specification_version: 4
126
125
  summary: Easily define simple models that can be easily serialized and de-serialized.