smart_properties 1.13.1 → 1.14.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: b393bac2287b2fc8bc96acdfe0da75ee774d9193
4
- data.tar.gz: f6cc1a80370cdd4d2c361c4eaf6b933841747a10
2
+ SHA256:
3
+ metadata.gz: 845948d63c5a647048b44f96e405808bdf3697430323a9ebf29b57c13ed157c9
4
+ data.tar.gz: be7cc4c03ab7bcba3dbdaea8a04676ada44f50f07d3f5a323926098af6560c97
5
5
  SHA512:
6
- metadata.gz: f878809a07157695137651ad2a20daf44b7dbbec3beae5685fc05c9512f6c165a366d6cf9b67256912bc8e1e50acb3fff98245997364560e894d35a816f46c0d
7
- data.tar.gz: 70571022057eb569d0b0702e25218d99a052be2506fb63d43262ef31ce869a05f462b38e3dbce681f2368b52516f4321328207cb40f9a0311df5cd9989cb8f8b
6
+ metadata.gz: fc5370fce67258915440c7e6fe3b02b1207d04ab1716be3f22b478f7ebf1d22807115b2f7aa7639b82030ad4309a69941606b20d6a64815e9afacd4fb48cdafb
7
+ data.tar.gz: 003da1d3daab182873e5ccd4ba6359707309fc06161f095c52803cfd6e1d3cb055c0baa59886aaed8b51f3eb5d05d4c6efdfc6188445a6e6de8d4c48a607b8ec
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.2
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(Number)
181
+ end
182
+ ```
183
+
174
184
  #### Default values
175
185
 
176
186
  There is also support for default values. Simply use the `:default`
@@ -171,3 +171,4 @@ require_relative 'smart_properties/property_collection'
171
171
  require_relative 'smart_properties/property'
172
172
  require_relative 'smart_properties/errors'
173
173
  require_relative 'smart_properties/version'
174
+ require_relative 'smart_properties/validations'
@@ -7,6 +7,7 @@ module SmartProperties
7
7
  attr_reader :accepter
8
8
  attr_reader :reader
9
9
  attr_reader :instance_variable_name
10
+ attr_reader :writable
10
11
 
11
12
  def self.define(scope, name, options = {})
12
13
  new(name, options).tap { |p| p.define(scope) }
@@ -21,6 +22,7 @@ module SmartProperties
21
22
  @accepter = attrs.delete(:accepts)
22
23
  @required = attrs.delete(:required)
23
24
  @reader = attrs.delete(:reader)
25
+ @writable = attrs.delete(:writable)
24
26
  @reader ||= @name
25
27
 
26
28
  @instance_variable_name = :"@#{name}"
@@ -46,6 +48,11 @@ module SmartProperties
46
48
  !null_object?(get(scope))
47
49
  end
48
50
 
51
+ def writable?
52
+ return true if @writable.nil?
53
+ @writable
54
+ end
55
+
49
56
  def convert(scope, value)
50
57
  return value unless converter
51
58
  return value if null_object?(value)
@@ -98,8 +105,11 @@ module SmartProperties
98
105
  scope.send(:define_method, reader) do
99
106
  property.get(self)
100
107
  end
101
- scope.send(:define_method, :"#{name}=") do |value|
102
- property.set(self, value)
108
+
109
+ if writable?
110
+ scope.send(:define_method, :"#{name}=") do |value|
111
+ property.set(self, value)
112
+ end
103
113
  end
104
114
  end
105
115
 
@@ -6,7 +6,9 @@ module SmartProperties
6
6
 
7
7
  def self.for(scope)
8
8
  parent = scope.ancestors[1..-1].find do |ancestor|
9
- ancestor.ancestors.include?(SmartProperties) && ancestor != SmartProperties
9
+ ancestor.ancestors.include?(SmartProperties) &&
10
+ ancestor != scope &&
11
+ ancestor != SmartProperties
10
12
  end
11
13
 
12
14
  if parent.nil?
@@ -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'
@@ -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
@@ -1,3 +1,3 @@
1
1
  module SmartProperties
2
- VERSION = "1.13.1"
2
+ VERSION = "1.14.0"
3
3
  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.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Tennhard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-29 00:00:00.000000000 Z
11
+ date: 2019-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -75,6 +75,8 @@ files:
75
75
  - lib/smart_properties/errors.rb
76
76
  - lib/smart_properties/property.rb
77
77
  - lib/smart_properties/property_collection.rb
78
+ - lib/smart_properties/validations.rb
79
+ - lib/smart_properties/validations/ancestor.rb
78
80
  - lib/smart_properties/version.rb
79
81
  - smart_properties.gemspec
80
82
  - spec/acceptance_checking_spec.rb
@@ -89,6 +91,8 @@ files:
89
91
  - spec/spec_helper.rb
90
92
  - spec/support/dummy_class.rb
91
93
  - spec/support/smart_property_matcher.rb
94
+ - spec/validations/ancestor_validation_spec.rb
95
+ - spec/writable_spec.rb
92
96
  homepage: ''
93
97
  licenses: []
94
98
  metadata: {}
@@ -107,8 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
111
  - !ruby/object:Gem::Version
108
112
  version: '0'
109
113
  requirements: []
110
- rubyforge_project:
111
- rubygems_version: 2.6.11
114
+ rubygems_version: 3.0.3
112
115
  signing_key:
113
116
  specification_version: 4
114
117
  summary: SmartProperties – Ruby accessors on steroids
@@ -125,3 +128,5 @@ test_files:
125
128
  - spec/spec_helper.rb
126
129
  - spec/support/dummy_class.rb
127
130
  - spec/support/smart_property_matcher.rb
131
+ - spec/validations/ancestor_validation_spec.rb
132
+ - spec/writable_spec.rb