smart_properties 1.13.1 → 1.14.0

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 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