smart_properties 1.13.1 → 1.16.1
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 +5 -5
- data/.github/workflows/testing.yml +24 -0
- data/.gitignore +2 -1
- data/.ruby-version +1 -1
- data/README.md +10 -0
- data/lib/smart_properties/property.rb +32 -9
- data/lib/smart_properties/property_collection.rb +11 -8
- data/lib/smart_properties/validations/ancestor.rb +27 -0
- data/lib/smart_properties/validations.rb +8 -0
- data/lib/smart_properties/version.rb +1 -1
- data/lib/smart_properties.rb +9 -0
- data/smart_properties.gemspec +5 -1
- data/spec/base_spec.rb +2 -2
- data/spec/configuration_error_spec.rb +33 -0
- data/spec/default_values_spec.rb +68 -0
- data/spec/inheritance_spec.rb +40 -0
- data/spec/property_collection_caching_spec.rb +10 -0
- data/spec/validations/ancestor_validation_spec.rb +62 -0
- data/spec/writable_spec.rb +39 -0
- metadata +17 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 044a13ef450738b8342644983451e7659ba6e33116801be84206fa9197e42c94
|
4
|
+
data.tar.gz: 709f79b9de54975dc92c8b47bb68db67b81c14f20f60d21358098060b34629db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
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
|
-
|
102
|
-
|
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.
|
145
|
-
|
146
|
-
#
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
-
|
9
|
-
ancestor.ancestors.include?(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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
parent.properties.register(collection
|
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
|
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
|
data/lib/smart_properties.rb
CHANGED
@@ -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'
|
data/smart_properties.gemspec
CHANGED
@@ -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", "~>
|
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(
|
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
|
data/spec/default_values_spec.rb
CHANGED
@@ -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
|
data/spec/inheritance_spec.rb
CHANGED
@@ -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.
|
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:
|
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: '
|
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: '
|
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
|
-
|
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
|
-
|
111
|
-
|
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
|