smart_properties 1.13.1 → 1.16.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b393bac2287b2fc8bc96acdfe0da75ee774d9193
4
- data.tar.gz: f6cc1a80370cdd4d2c361c4eaf6b933841747a10
2
+ SHA256:
3
+ metadata.gz: 044a13ef450738b8342644983451e7659ba6e33116801be84206fa9197e42c94
4
+ data.tar.gz: 709f79b9de54975dc92c8b47bb68db67b81c14f20f60d21358098060b34629db
5
5
  SHA512:
6
- metadata.gz: f878809a07157695137651ad2a20daf44b7dbbec3beae5685fc05c9512f6c165a366d6cf9b67256912bc8e1e50acb3fff98245997364560e894d35a816f46c0d
7
- data.tar.gz: 70571022057eb569d0b0702e25218d99a052be2506fb63d43262ef31ce869a05f462b38e3dbce681f2368b52516f4321328207cb40f9a0311df5cd9989cb8f8b
6
+ metadata.gz: 0a9d6e5383e26cc9143553fe54c5f030be3d40d1927b4613f0f87c751fb3fdf3741afb0aeaaf22336a229070f3e05a1bbe25751736b50e7f8cf3d4d1c8b74909
7
+ data.tar.gz: bf90c5eec8fa7059a044f14b810583492d3775f254c3a59c0cf1985b0a2343fa2830777a0c6e28b91b2e7eed5ca528189862f6effa8724d094775168cf17d0a8
@@ -0,0 +1,24 @@
1
+ name: Testing
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ test:
12
+
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - name: Set up Ruby
18
+ uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: 2.6
21
+ - name: Install dependencies
22
+ run: bundle install
23
+ - name: Run tests
24
+ run: bundle exec rake
data/.gitignore CHANGED
@@ -15,6 +15,7 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .idea/
18
19
 
19
20
  .rspec
20
- .rvmrc
21
+ .rvmrc
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.4.1
1
+ 2.6.6
data/README.md CHANGED
@@ -171,6 +171,16 @@ class Article
171
171
  end
172
172
  ```
173
173
 
174
+ There are also a set of common validation helpers you may use. These common
175
+ cases are provided to help avoid rewriting validation logic that occurs
176
+ often. These validations can be found in the `SmartProperties::Validations` module.
177
+
178
+ ```ruby
179
+ class Article
180
+ property :view_count, accepts: Ancestor.must_be(type: Number)
181
+ end
182
+ ```
183
+
174
184
  #### Default values
175
185
 
176
186
  There is also support for default values. Simply use the `:default`
@@ -1,12 +1,14 @@
1
1
  module SmartProperties
2
2
  class Property
3
3
  MODULE_REFERENCE = :"@_smart_properties_method_scope"
4
+ ALLOWED_DEFAULT_CLASSES = [Proc, Numeric, String, Range, TrueClass, FalseClass, NilClass, Symbol, Module].freeze
4
5
 
5
6
  attr_reader :name
6
7
  attr_reader :converter
7
8
  attr_reader :accepter
8
9
  attr_reader :reader
9
10
  attr_reader :instance_variable_name
11
+ attr_reader :writable
10
12
 
11
13
  def self.define(scope, name, options = {})
12
14
  new(name, options).tap { |p| p.define(scope) }
@@ -21,10 +23,16 @@ module SmartProperties
21
23
  @accepter = attrs.delete(:accepts)
22
24
  @required = attrs.delete(:required)
23
25
  @reader = attrs.delete(:reader)
26
+ @writable = attrs.delete(:writable)
24
27
  @reader ||= @name
25
28
 
26
29
  @instance_variable_name = :"@#{name}"
27
30
 
31
+ unless ALLOWED_DEFAULT_CLASSES.any? { |cls| @default.is_a?(cls) }
32
+ raise ConfigurationError, "Default attribute value #{@default.inspect} cannot be specified as literal, "\
33
+ "use the syntax `default: -> { ... }` instead."
34
+ end
35
+
28
36
  unless attrs.empty?
29
37
  raise ConfigurationError, "SmartProperties do not support the following configuration options: #{attrs.keys.map { |m| m.to_s }.sort.join(', ')}."
30
38
  end
@@ -46,6 +54,11 @@ module SmartProperties
46
54
  !null_object?(get(scope))
47
55
  end
48
56
 
57
+ def writable?
58
+ return true if @writable.nil?
59
+ @writable
60
+ end
61
+
49
62
  def convert(scope, value)
50
63
  return value unless converter
51
64
  return value if null_object?(value)
@@ -59,7 +72,7 @@ module SmartProperties
59
72
  end
60
73
 
61
74
  def default(scope)
62
- @default.kind_of?(Proc) ? scope.instance_exec(&@default) : @default
75
+ @default.kind_of?(Proc) ? scope.instance_exec(&@default) : @default.dup
63
76
  end
64
77
 
65
78
  def accepts?(value, scope)
@@ -98,8 +111,11 @@ module SmartProperties
98
111
  scope.send(:define_method, reader) do
99
112
  property.get(self)
100
113
  end
101
- scope.send(:define_method, :"#{name}=") do |value|
102
- property.set(self, value)
114
+
115
+ if writable?
116
+ scope.send(:define_method, :"#{name}=") do |value|
117
+ property.set(self, value)
118
+ end
103
119
  end
104
120
  end
105
121
 
@@ -141,12 +157,19 @@ module SmartProperties
141
157
  rescue NoMethodError => error
142
158
  # BasicObject does not respond to #nil? by default, so we need to double
143
159
  # check if somebody implemented it and it fails internally or if the
144
- # error occured because the method is actually not present. In the former
145
- # case, we want to raise the exception because there is something wrong
146
- # with the implementation of object#nil?. In the latter case we treat the
147
- # object as truthy because we don't know better.
148
- raise error if (class << object; self; end).public_instance_methods.include?(:nil?)
149
- false
160
+ # error occured because the method is actually not present.
161
+
162
+ # This is a workaround for the fact that #singleton_class is defined on Object, but not BasicObject.
163
+ the_singleton_class = (class << object; self; end)
164
+
165
+ if the_singleton_class.public_instance_methods.include?(:nil?)
166
+ # object defines #nil?, but it raised NoMethodError,
167
+ # something is wrong with the implementation, so raise the exception.
168
+ raise error
169
+ else
170
+ # treat the object as truthy because we don't know better.
171
+ false
172
+ end
150
173
  end
151
174
  end
152
175
  end
@@ -5,16 +5,19 @@ module SmartProperties
5
5
  attr_reader :parent
6
6
 
7
7
  def self.for(scope)
8
- parent = scope.ancestors[1..-1].find do |ancestor|
9
- ancestor.ancestors.include?(SmartProperties) && ancestor != SmartProperties
8
+ parents = scope.ancestors[1..-1].select do |ancestor|
9
+ ancestor.ancestors.include?(SmartProperties) &&
10
+ ancestor != scope &&
11
+ ancestor != SmartProperties
10
12
  end
11
13
 
12
- if parent.nil?
13
- new
14
- else
15
- parent.properties.register(collection = new)
16
- collection
14
+ collection = new
15
+
16
+ parents.each do |parent|
17
+ parent.properties.register(collection)
17
18
  end
19
+
20
+ collection
18
21
  end
19
22
 
20
23
  def initialize
@@ -73,7 +76,7 @@ module SmartProperties
73
76
  end
74
77
 
75
78
  def refresh(parent_collection)
76
- @collection_with_parent_collection = parent_collection.merge(collection)
79
+ @collection_with_parent_collection.merge!(parent_collection)
77
80
  notify_children
78
81
  nil
79
82
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ module SmartProperties
3
+ module Validations
4
+ class Ancestor
5
+ include SmartProperties
6
+
7
+ property! :type, accepts: ->(type) { type.is_a?(Class) }
8
+
9
+ def validate(klass)
10
+ klass.is_a?(Class) && klass < type
11
+ end
12
+
13
+ def to_proc
14
+ validator = self
15
+ ->(klass) { validator.validate(klass) }
16
+ end
17
+
18
+ def to_s
19
+ "subclasses of #{type.to_s}"
20
+ end
21
+
22
+ class << self
23
+ alias_method :must_be, :new
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmartProperties
4
+ module Validations
5
+ end
6
+ end
7
+
8
+ require_relative 'validations/ancestor'
@@ -1,3 +1,3 @@
1
1
  module SmartProperties
2
- VERSION = "1.13.1"
2
+ VERSION = "1.16.1"
3
3
  end
@@ -91,6 +91,13 @@ module SmartProperties
91
91
  protected :property!
92
92
  end
93
93
 
94
+ module ModuleMethods
95
+ def included(target)
96
+ super
97
+ target.include(SmartProperties)
98
+ end
99
+ end
100
+
94
101
  class << self
95
102
  private
96
103
 
@@ -102,6 +109,7 @@ module SmartProperties
102
109
  #
103
110
  def included(base)
104
111
  base.extend(ClassMethods)
112
+ base.extend(ModuleMethods) if base.is_a?(Module)
105
113
  end
106
114
  end
107
115
 
@@ -171,3 +179,4 @@ require_relative 'smart_properties/property_collection'
171
179
  require_relative 'smart_properties/property'
172
180
  require_relative 'smart_properties/errors'
173
181
  require_relative 'smart_properties/version'
182
+ require_relative 'smart_properties/validations'
@@ -12,6 +12,10 @@ Gem::Specification.new do |gem|
12
12
  gem.summary = %q{SmartProperties – Ruby accessors on steroids}
13
13
  gem.homepage = ""
14
14
 
15
+ gem.metadata = {
16
+ "source_code_uri" => "https://github.com/t6d/smart_properties"
17
+ }
18
+
15
19
  gem.files = `git ls-files`.split($\)
16
20
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
21
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
@@ -20,6 +24,6 @@ Gem::Specification.new do |gem|
20
24
  gem.version = SmartProperties::VERSION
21
25
 
22
26
  gem.add_development_dependency "rspec", "~> 3.0"
23
- gem.add_development_dependency "rake", "~> 10.0"
27
+ gem.add_development_dependency "rake", "~> 13.0"
24
28
  gem.add_development_dependency "pry"
25
29
  end
data/spec/base_spec.rb CHANGED
@@ -41,7 +41,7 @@ RSpec.describe SmartProperties do
41
41
  default_title = double(to_title: 'chunky')
42
42
 
43
43
  DummyClass.new do
44
- property :title, converts: :to_title, accepts: String, required: true, default: default_title
44
+ property :title, converts: :to_title, accepts: String, required: true, default: -> { default_title }
45
45
  end
46
46
  end
47
47
 
@@ -49,7 +49,7 @@ RSpec.describe SmartProperties do
49
49
 
50
50
  it "should return a property's configuration with #to_h" do
51
51
  expect(klass.properties.values.first.to_h).to match(
52
- accepter: String, converter: :to_title, default: an_instance_of(RSpec::Mocks::Double),
52
+ accepter: String, converter: :to_title, default: an_instance_of(Proc),
53
53
  instance_variable_name: :@title, name: :title, reader: :title, required: true
54
54
  )
55
55
  end
@@ -13,5 +13,38 @@ RSpec.describe SmartProperties, 'configuration error' do
13
13
 
14
14
  expect(&invalid_property_definition).to raise_error(SmartProperties::ConfigurationError, "SmartProperties do not support the following configuration options: invalid_option_1, invalid_option_2, invalid_option_3.")
15
15
  end
16
+
17
+ it "should accept default values that can't be mutated" do
18
+ valid_property_definition = lambda do
19
+ klass.class_eval do
20
+ property :proc, default: -> { }
21
+ property :numeric_float, default: 1.23
22
+ property :numeric_int, default: 456
23
+ property :string, default: "abc"
24
+ property :range, default: 123...456
25
+ property :bool_true, default: true
26
+ property :bool_false, default: false
27
+ property :nil, default: nil
28
+ property :symbol, default: :abc
29
+ property :module, default: Integer
30
+ end
31
+ end
32
+
33
+ expect(&valid_property_definition).not_to raise_error
34
+ end
35
+
36
+ it "should not accept default values that may be mutated" do
37
+ invalid_property_definition = lambda do
38
+ klass.class_eval do
39
+ property :title, default: []
40
+ end
41
+ end
42
+
43
+ expect(&invalid_property_definition).to(
44
+ raise_error(SmartProperties::ConfigurationError,
45
+ "Default attribute value [] cannot be specified as literal, "\
46
+ "use the syntax `default: -> { ... }` instead.")
47
+ )
48
+ end
16
49
  end
17
50
  end
@@ -48,4 +48,72 @@ RSpec.describe SmartProperties, 'default values' do
48
48
  end
49
49
  end
50
50
  end
51
+
52
+ context "when defining a new property with a literal default value" do
53
+ context 'with a numeric default' do
54
+ subject(:klass) { DummyClass.new { property :var, default: 123 } }
55
+
56
+ it 'accepts the default and returns it' do
57
+ instance = klass.new
58
+ expect(instance.var).to(be(123))
59
+ end
60
+ end
61
+
62
+ context 'with a string default' do
63
+ DEFAULT_VALUE = 'a string'
64
+ subject(:klass) { DummyClass.new { property :var, default: DEFAULT_VALUE } }
65
+
66
+ it 'accepts the default and returns it' do
67
+ instance = klass.new
68
+ expect(instance.var).to(eq(DEFAULT_VALUE))
69
+ end
70
+
71
+ it 'returns a copy of the string' do
72
+ instance = klass.new
73
+ expect(instance.var).to_not(be(DEFAULT_VALUE))
74
+ end
75
+
76
+ it 'mutating the instance variable does not mutate the original' do
77
+ instance = klass.new
78
+ instance.var[0] = 'o'
79
+ expect(DEFAULT_VALUE).to(eq('a string'))
80
+ end
81
+ end
82
+
83
+ context 'with a range default' do
84
+ subject(:klass) { DummyClass.new { property :var, default: 1..2 } }
85
+
86
+ it 'accepts the default and returns it' do
87
+ instance = klass.new
88
+ expect(instance.var).to(eq(1..2))
89
+ end
90
+ end
91
+
92
+ context 'with a true default' do
93
+ subject(:klass) { DummyClass.new { property :var, default: true } }
94
+
95
+ it 'accepts the default and returns it' do
96
+ instance = klass.new
97
+ expect(instance.var).to(be(true))
98
+ end
99
+ end
100
+
101
+ context 'with a false default' do
102
+ subject(:klass) { DummyClass.new { property :var, default: false } }
103
+
104
+ it 'accepts the default and returns it' do
105
+ instance = klass.new
106
+ expect(instance.var).to(be(false))
107
+ end
108
+ end
109
+
110
+ context 'with a symbol default' do
111
+ subject(:klass) { DummyClass.new { property :var, default: :foo } }
112
+
113
+ it 'accepts the default and returns it' do
114
+ instance = klass.new
115
+ expect(instance.var).to(be(:foo))
116
+ end
117
+ end
118
+ end
51
119
  end
@@ -225,4 +225,44 @@ RSpec.describe SmartProperties, 'intheritance' do
225
225
  end
226
226
  end
227
227
  end
228
+
229
+ it "supports multiple inheritance through modules" do
230
+ m = Module.new do
231
+ include SmartProperties
232
+ property :m, default: 1
233
+ end
234
+
235
+ n = Module.new do
236
+ include SmartProperties
237
+ property :n, default: 2
238
+ end
239
+
240
+ o = Module.new {}
241
+
242
+ klass = Class.new do
243
+ include m
244
+ include o
245
+ include n
246
+ end
247
+
248
+ n.module_eval do
249
+ property :p, default: 3
250
+ end
251
+
252
+ instance = klass.new
253
+
254
+ expect(instance.m).to eq(1)
255
+ expect(instance.n).to eq(2)
256
+ expect(instance.p).to eq(3)
257
+
258
+ expect { klass.new(m: 4, n: 5, p: 6) }.to_not raise_error
259
+
260
+ klass2 = Class.new do
261
+ include n
262
+ include m
263
+ end
264
+
265
+ expect { klass.new(m: 4, n: 5, p: 6) }.to_not raise_error
266
+ expect { klass2.new(m: 4, n: 5, p: 6) }.to_not raise_error
267
+ end
228
268
  end
@@ -26,4 +26,14 @@ RSpec.describe SmartProperties, "property collection caching:" do
26
26
  expect(subsubclass.properties.keys - expected_names).to be_empty
27
27
  expect(subsubclass.properties.to_hash.keys - expected_names).to be_empty
28
28
  end
29
+
30
+ specify "a SmartProperty enabled object should not check itself for properties if prepended" do
31
+ expect do
32
+ base_class = DummyClass.new {
33
+ prepend Module.new
34
+ property :title
35
+ }
36
+ expect(base_class.new).to have_smart_property(:title)
37
+ end.not_to raise_error(SystemStackError)
38
+ end
29
39
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+ require 'smart_properties/validations/ancestor'
4
+
5
+ RSpec.describe SmartProperties::Validations::Ancestor, 'validates ancestor' do
6
+ context 'used to validate the ancestor of a smart_properties value' do
7
+ let!(:test_base) do
8
+ Class.new do
9
+ def self.to_s
10
+ 'TestBase'
11
+ end
12
+ end
13
+ end
14
+
15
+ subject(:klass) do
16
+ test_base_ref = test_base
17
+
18
+ DummyClass.new do
19
+ property :visible, accepts: SmartProperties::Validations::Ancestor.must_be(type: test_base_ref)
20
+ end
21
+ end
22
+
23
+ it 'should return an error for any non Class based value' do
24
+ expect { subject.new(visible: true) }
25
+ .to raise_error(SmartProperties::InvalidValueError, /Only accepts: subclasses of TestBase/)
26
+ end
27
+
28
+ it 'should return an error for any Class instance instead of a class type' do
29
+ non_ancestor_class = Class.new
30
+
31
+ expect { subject.new(visible: non_ancestor_class.new) }
32
+ .to raise_error(SmartProperties::InvalidValueError, /Only accepts: subclasses of TestBase/)
33
+ end
34
+
35
+
36
+ it 'should return an error for any Class instance instead of a class type even if it is a child' do
37
+ test_base_child = Class.new(test_base)
38
+
39
+ expect { subject.new(visible: test_base_child.new) }
40
+ .to raise_error(SmartProperties::InvalidValueError, /Only accepts: subclasses of TestBase/)
41
+ end
42
+
43
+ it 'should return an error for a Class type that is not a child of the required ancestor' do
44
+ non_ancestor_class = Class.new
45
+
46
+ expect { subject.new(visible: non_ancestor_class) }
47
+ .to raise_error(SmartProperties::InvalidValueError, /Only accepts: subclasses of TestBase/)
48
+ end
49
+
50
+ it 'should return an error if the class is the ancestor itself' do
51
+ expect { subject.new(visible: test_base) }
52
+ .to raise_error(SmartProperties::InvalidValueError, /Only accepts: subclasses of TestBase/)
53
+ end
54
+
55
+ it 'should succeed if the given class is a subtype ' do
56
+ test_valid_class = Class.new(test_base)
57
+
58
+ expect { subject.new(visible: test_valid_class) }
59
+ .not_to raise_error
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe SmartProperties, 'writable properties' do
6
+ context 'when a property is defined as not writable there should be no accessor for the property' do
7
+ subject(:klass) { DummyClass.new { property :id, writable: false } }
8
+
9
+ it "should throw a no method error when trying to set the property" do
10
+ new_class_instance = klass.new(id: 42)
11
+
12
+ expect(new_class_instance.id).to eq(42)
13
+ expect { new_class_instance.id = 50 }.to raise_error(NoMethodError)
14
+ expect(new_class_instance.id).to eq(42)
15
+ end
16
+ end
17
+
18
+ context 'when a property is defined as writable there should be an accessor available' do
19
+ subject(:klass) { DummyClass.new { property :id, writable: true } }
20
+
21
+ it "should allow changing of the property" do
22
+ new_class_instance = klass.new(id: 42)
23
+
24
+ new_class_instance.id = 50
25
+ expect(new_class_instance.id).to eq(50)
26
+ end
27
+ end
28
+
29
+ context 'when writable is not defined on the property it should default to being writable' do
30
+ subject(:klass) { DummyClass.new { property :id } }
31
+
32
+ it "should allow changing of the property" do
33
+ new_class_instance = klass.new(id: 42)
34
+
35
+ new_class_instance.id = 50
36
+ expect(new_class_instance.id).to eq(50)
37
+ end
38
+ end
39
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_properties
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.1
4
+ version: 1.16.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Tennhard
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-29 00:00:00.000000000 Z
11
+ date: 2021-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '13.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: '10.0'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: pry
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -62,6 +62,7 @@ executables: []
62
62
  extensions: []
63
63
  extra_rdoc_files: []
64
64
  files:
65
+ - ".github/workflows/testing.yml"
65
66
  - ".gitignore"
66
67
  - ".ruby-version"
67
68
  - ".yardopts"
@@ -75,6 +76,8 @@ files:
75
76
  - lib/smart_properties/errors.rb
76
77
  - lib/smart_properties/property.rb
77
78
  - lib/smart_properties/property_collection.rb
79
+ - lib/smart_properties/validations.rb
80
+ - lib/smart_properties/validations/ancestor.rb
78
81
  - lib/smart_properties/version.rb
79
82
  - smart_properties.gemspec
80
83
  - spec/acceptance_checking_spec.rb
@@ -89,10 +92,13 @@ files:
89
92
  - spec/spec_helper.rb
90
93
  - spec/support/dummy_class.rb
91
94
  - spec/support/smart_property_matcher.rb
95
+ - spec/validations/ancestor_validation_spec.rb
96
+ - spec/writable_spec.rb
92
97
  homepage: ''
93
98
  licenses: []
94
- metadata: {}
95
- post_install_message:
99
+ metadata:
100
+ source_code_uri: https://github.com/t6d/smart_properties
101
+ post_install_message:
96
102
  rdoc_options: []
97
103
  require_paths:
98
104
  - lib
@@ -107,9 +113,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
113
  - !ruby/object:Gem::Version
108
114
  version: '0'
109
115
  requirements: []
110
- rubyforge_project:
111
- rubygems_version: 2.6.11
112
- signing_key:
116
+ rubygems_version: 3.2.20
117
+ signing_key:
113
118
  specification_version: 4
114
119
  summary: SmartProperties – Ruby accessors on steroids
115
120
  test_files:
@@ -125,3 +130,5 @@ test_files:
125
130
  - spec/spec_helper.rb
126
131
  - spec/support/dummy_class.rb
127
132
  - spec/support/smart_property_matcher.rb
133
+ - spec/validations/ancestor_validation_spec.rb
134
+ - spec/writable_spec.rb