smart_properties 1.2.3 → 1.3.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 +7 -0
- data/README.md +91 -43
- data/lib/smart_properties.rb +7 -11
- data/spec/smart_properties_spec.rb +34 -1
- metadata +23 -36
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1ad423c03336ecabd1643f2cfea0faacc7e74f68
|
4
|
+
data.tar.gz: 5b1f77117faaa6314f0a4fe62952367cf3f13fcd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cb13998cb8ca5404110a2596dbc58442a13554d75026d44668759a35b7c3a942de99b3c80f32465917e5e4ff3c7a414f194768dc78ceb49a8eea89901e14c905
|
7
|
+
data.tar.gz: 61d44649397e83afb2c1abb20af45c01e7aac716e73089720d210b5faeb798c6d6695cc627287c564cb9b41f642c1383e72a52df7bb656cea3c38ce7040dbbda
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# Smartproperties
|
2
2
|
|
3
3
|
Ruby accessors on steroids.
|
4
4
|
|
@@ -6,15 +6,21 @@ Ruby accessors on steroids.
|
|
6
6
|
|
7
7
|
Add this line to your application's Gemfile:
|
8
8
|
|
9
|
-
|
9
|
+
```ruby
|
10
|
+
gem 'smart_properties'
|
11
|
+
```
|
10
12
|
|
11
13
|
And then execute:
|
12
14
|
|
13
|
-
|
15
|
+
```plain
|
16
|
+
$ bundle
|
17
|
+
```
|
14
18
|
|
15
19
|
Or install it yourself as:
|
16
20
|
|
17
|
-
|
21
|
+
```plain
|
22
|
+
$ gem install smart_properties
|
23
|
+
```
|
18
24
|
|
19
25
|
## Usage
|
20
26
|
|
@@ -45,32 +51,45 @@ The example below shows how to implement a class called `Message` which has
|
|
45
51
|
three properties: `subject`, `body`, and `priority`. The two properties,
|
46
52
|
`subject` and `priority`, are required whereas `body` is optional.
|
47
53
|
Furthermore, all properties use input conversion. The `priority` property also
|
48
|
-
uses validation and has a default value
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
54
|
+
uses validation and has a default value so if it is not set during initialization
|
55
|
+
it will be set according to the default value.
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
require 'rubygems'
|
59
|
+
require 'smart_properties'
|
60
|
+
|
61
|
+
class Message
|
62
|
+
include SmartProperties
|
63
|
+
|
64
|
+
property :subject, :converts => :to_s,
|
65
|
+
:required => true
|
66
|
+
|
67
|
+
property :body, :converts => :to_s
|
68
|
+
|
69
|
+
property :priority, :converts => :to_sym,
|
70
|
+
:accepts => [:low, :normal, :high],
|
71
|
+
:default => :normal,
|
72
|
+
:required => true
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
61
76
|
Creating an instance of this class without specifying any attributes will
|
62
77
|
result in an `ArgumentError` telling you to specify the required property
|
63
78
|
`subject`.
|
64
79
|
|
65
|
-
|
80
|
+
```ruby
|
81
|
+
Message.new # => raises ArgumentError, "Message requires the property subject to be set"
|
82
|
+
```
|
66
83
|
|
67
84
|
Providing the constructor with a title but with an invalid value for the
|
68
85
|
property `priority` will also result in an `ArgumentError` telling you to
|
69
86
|
provide a proper value for the property `priority`.
|
70
87
|
|
71
|
-
|
72
|
-
|
73
|
-
|
88
|
+
```ruby
|
89
|
+
m = Message.new :subject => 'Lorem ipsum'
|
90
|
+
m.priority # => :normal
|
91
|
+
m.priority = :urgent # => raises ArgumentError, Message does not accept :urgent as value for the property priority
|
92
|
+
```
|
74
93
|
|
75
94
|
Next, we discuss the various configuration options `SmartProperties` provide.
|
76
95
|
|
@@ -89,9 +108,11 @@ the result of this method call as value instead. The example below shows how
|
|
89
108
|
to implement a property that automatically converts all given input to a
|
90
109
|
`String` by calling `#to_s` on the object provided as input.
|
91
110
|
|
92
|
-
|
93
|
-
|
94
|
-
|
111
|
+
```ruby
|
112
|
+
class Article
|
113
|
+
property :title, :converts => :to_s
|
114
|
+
end
|
115
|
+
```
|
95
116
|
|
96
117
|
If you need more fine-grained control, you can use a lambda statement to
|
97
118
|
specify how the conversion should be done. The statement will be evaluated in
|
@@ -99,9 +120,11 @@ the context of the class defining the property and takes the given value as
|
|
99
120
|
input. The example below shows how to implement a property that automatically
|
100
121
|
converts all given input to a slug representation.
|
101
122
|
|
102
|
-
|
103
|
-
|
104
|
-
|
123
|
+
```ruby
|
124
|
+
class Article
|
125
|
+
property :slug, :converts => lambda { |slug| slug.downcase.gsub(/\s+/, '-').gsub(/\W/, '') }
|
126
|
+
end
|
127
|
+
```
|
105
128
|
|
106
129
|
#### Input validation
|
107
130
|
|
@@ -111,17 +134,21 @@ automatic validation whenever the setter for a certain property is called. The
|
|
111
134
|
example below shows how to implement a property which only accepts instances
|
112
135
|
of type `String` as input.
|
113
136
|
|
114
|
-
|
115
|
-
|
116
|
-
|
137
|
+
```ruby
|
138
|
+
class Article
|
139
|
+
property :title, :accepts => String
|
140
|
+
end
|
141
|
+
```
|
117
142
|
|
118
143
|
Instead of using a class, you can also use a list of permitted values. The
|
119
144
|
example below shows how to implement a property that only accepts `true` or
|
120
145
|
`false` as values.
|
121
146
|
|
122
|
-
|
123
|
-
|
124
|
-
|
147
|
+
```ruby
|
148
|
+
class Article
|
149
|
+
property :published, :accepts => [true, false]
|
150
|
+
end
|
151
|
+
```
|
125
152
|
|
126
153
|
You can also use a `lambda` statement for input validation if a more complex
|
127
154
|
validation procedure is required. The `lambda` statement is evaluated in the
|
@@ -129,9 +156,11 @@ context of the class that defines the property and receives the given value as
|
|
129
156
|
input. The example below shows how to implement a property called title that
|
130
157
|
only accepts values which match the given regular expression.
|
131
158
|
|
132
|
-
|
133
|
-
|
134
|
-
|
159
|
+
```ruby
|
160
|
+
class Article
|
161
|
+
property :title, :accepts => lambda { |title| /^Lorem \w+$/ =~ title }
|
162
|
+
end
|
163
|
+
```
|
135
164
|
|
136
165
|
#### Default values
|
137
166
|
|
@@ -140,20 +169,39 @@ configuration parameter to configure a default value for a certain property.
|
|
140
169
|
The example below demonstrates how to implement a property that has 42 as
|
141
170
|
default value.
|
142
171
|
|
143
|
-
|
144
|
-
|
145
|
-
|
172
|
+
```ruby
|
173
|
+
class Article
|
174
|
+
property :id, :default => 42
|
175
|
+
end
|
176
|
+
```
|
177
|
+
|
178
|
+
Default values can also be specified using blocks which are evaluated at
|
179
|
+
runtime.
|
146
180
|
|
147
181
|
#### Presence checking
|
148
182
|
|
149
|
-
To ensure that a property is always set
|
183
|
+
To ensure that a property is always set and never `nil`, you can use the
|
150
184
|
`:required` configuration parameter. If present, this parameter will instruct
|
151
185
|
the setter of a property to not accept nil as input. The example below shows
|
152
186
|
how to implement a property that may not be `nil`.
|
153
187
|
|
154
|
-
|
155
|
-
|
156
|
-
|
188
|
+
```ruby
|
189
|
+
class Article
|
190
|
+
property :title, :required => true
|
191
|
+
end
|
192
|
+
```
|
193
|
+
|
194
|
+
The decision whether or not a property is required can also be delayed and
|
195
|
+
evaluated at runtime by providing a block instead of a boolean value. The
|
196
|
+
example below shows how to implement a class that has two properties, `name`
|
197
|
+
and `anonoymous`. The `name` is only required if `anonymous` is set to `false`.
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
class Person
|
201
|
+
property :name, required: lambda { not anonymous }
|
202
|
+
property :anonymous, required: true, default: true, accepts: [true, false]
|
203
|
+
end
|
204
|
+
```
|
157
205
|
|
158
206
|
## Contributing
|
159
207
|
|
data/lib/smart_properties.rb
CHANGED
@@ -22,7 +22,7 @@
|
|
22
22
|
#
|
23
23
|
module SmartProperties
|
24
24
|
|
25
|
-
VERSION = "1.
|
25
|
+
VERSION = "1.3.0"
|
26
26
|
|
27
27
|
class Property
|
28
28
|
|
@@ -37,19 +37,15 @@ module SmartProperties
|
|
37
37
|
@default = attrs.delete(:default)
|
38
38
|
@converter = attrs.delete(:converts)
|
39
39
|
@accepter = attrs.delete(:accepts)
|
40
|
-
@required =
|
40
|
+
@required = attrs.delete(:required)
|
41
41
|
|
42
42
|
unless attrs.empty?
|
43
43
|
raise ArgumentError, "SmartProperties do not support the following configuration options: #{attrs.keys.map { |m| m.to_s }.sort.join(', ')}."
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
def required
|
48
|
-
@required
|
49
|
-
end
|
50
|
-
|
51
|
-
def required?
|
52
|
-
@required
|
47
|
+
def required?(scope)
|
48
|
+
@required.kind_of?(Proc) ? scope.instance_exec(&@required) : !!@required
|
53
49
|
end
|
54
50
|
|
55
51
|
def convert(value, scope)
|
@@ -84,7 +80,7 @@ module SmartProperties
|
|
84
80
|
end
|
85
81
|
|
86
82
|
def prepare(value, scope)
|
87
|
-
if required? && value.nil?
|
83
|
+
if required?(scope) && value.nil?
|
88
84
|
raise ArgumentError, "#{scope.class.name} requires the property #{self.name} to be set"
|
89
85
|
end
|
90
86
|
|
@@ -94,7 +90,7 @@ module SmartProperties
|
|
94
90
|
raise ArgumentError, "#{scope.class.name} does not accept #{value.inspect} as value for the property #{self.name}"
|
95
91
|
end
|
96
92
|
|
97
|
-
|
93
|
+
value
|
98
94
|
end
|
99
95
|
|
100
96
|
def define(klass)
|
@@ -259,7 +255,7 @@ module SmartProperties
|
|
259
255
|
block.call(self) if block
|
260
256
|
|
261
257
|
# Check presence of all required properties
|
262
|
-
faulty_properties = properties.select { |_, property| property.required? && send(property.name).nil? }
|
258
|
+
faulty_properties = properties.select { |_, property| property.required?(self) && send(property.name).nil? }
|
263
259
|
unless faulty_properties.empty?
|
264
260
|
raise ArgumentError, "#{self.class.name} requires the following properties to be set: #{faulty_properties.map { |_, property| property.name }.sort.join(' ')}"
|
265
261
|
end
|
@@ -11,7 +11,7 @@ describe SmartProperties do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
it "should add a .property method" do
|
14
|
-
subject.
|
14
|
+
subject.respond_to?(:property, true).should be_true
|
15
15
|
end
|
16
16
|
|
17
17
|
context "and defining a property with invalid configuration options" do
|
@@ -620,6 +620,39 @@ describe SmartProperties do
|
|
620
620
|
|
621
621
|
end
|
622
622
|
|
623
|
+
context "when building a class that has a property which is required depending on the value of another property" do
|
624
|
+
|
625
|
+
subject(:klass) do
|
626
|
+
described_class = self.described_class
|
627
|
+
|
628
|
+
Class.new do
|
629
|
+
include described_class
|
630
|
+
property :name, :required => lambda { not anonymous }
|
631
|
+
property :anonymous, accepts: [true, false], default: true
|
632
|
+
def self.name; "Dummy"; end
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
context "when created with no arguments" do
|
637
|
+
it "should not raise an error" do
|
638
|
+
expect { klass.new }.to_not raise_error
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
context "when created with no name and anonymous being set to false" do
|
643
|
+
it "should raise an error indicating that a required property was not specified" do
|
644
|
+
expect { klass.new anonymous: false }.to raise_error(ArgumentError, "Dummy requires the following properties to be set: name")
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
context "when created with a name and anonymous being set to false" do
|
649
|
+
it "should not raise an error" do
|
650
|
+
expect { klass.new name: "John Doe", anonymous: false }.to_not raise_error
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
end
|
655
|
+
|
623
656
|
context "when building a class that has a property which is required and has false as default" do
|
624
657
|
|
625
658
|
subject(:klass) do
|
metadata
CHANGED
@@ -1,67 +1,61 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_properties
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
version: 1.2.3
|
4
|
+
version: 1.3.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Konstantin Tennhard
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2013-08-04 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
|
-
|
16
|
-
|
17
|
-
none: false
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '2.12'
|
22
|
-
|
20
|
+
type: :development
|
23
21
|
prerelease: false
|
24
|
-
|
25
|
-
none: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '2.12'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
|
-
|
32
|
-
|
33
|
-
none: false
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
34
30
|
requirements:
|
35
31
|
- - ~>
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0.8'
|
38
|
-
|
34
|
+
type: :development
|
39
35
|
prerelease: false
|
40
|
-
|
41
|
-
none: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
37
|
requirements:
|
43
38
|
- - ~>
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0.8'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
|
-
|
48
|
-
|
49
|
-
none: false
|
42
|
+
name: guard-rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
50
44
|
requirements:
|
51
45
|
- - ~>
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0.7'
|
54
|
-
|
48
|
+
type: :development
|
55
49
|
prerelease: false
|
56
|
-
|
57
|
-
none: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
51
|
requirements:
|
59
52
|
- - ~>
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0.7'
|
62
|
-
description:
|
63
|
-
|
64
|
-
|
55
|
+
description: |2
|
56
|
+
SmartProperties are a more flexible and feature-rich alternative to
|
57
|
+
traditional Ruby accessors. They provide support for input conversion,
|
58
|
+
input validation, specifying default values and presence checking.
|
65
59
|
email:
|
66
60
|
- me@t6d.de
|
67
61
|
executables: []
|
@@ -83,33 +77,26 @@ files:
|
|
83
77
|
- spec/support/smart_property_matcher.rb
|
84
78
|
homepage: ''
|
85
79
|
licenses: []
|
80
|
+
metadata: {}
|
86
81
|
post_install_message:
|
87
82
|
rdoc_options: []
|
88
83
|
require_paths:
|
89
84
|
- lib
|
90
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
-
none: false
|
92
86
|
requirements:
|
93
|
-
- -
|
87
|
+
- - '>='
|
94
88
|
- !ruby/object:Gem::Version
|
95
|
-
segments:
|
96
|
-
- 0
|
97
|
-
hash: -4042209003338815638
|
98
89
|
version: '0'
|
99
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
-
none: false
|
101
91
|
requirements:
|
102
|
-
- -
|
92
|
+
- - '>='
|
103
93
|
- !ruby/object:Gem::Version
|
104
|
-
segments:
|
105
|
-
- 0
|
106
|
-
hash: -4042209003338815638
|
107
94
|
version: '0'
|
108
95
|
requirements: []
|
109
96
|
rubyforge_project:
|
110
|
-
rubygems_version:
|
97
|
+
rubygems_version: 2.0.6
|
111
98
|
signing_key:
|
112
|
-
specification_version:
|
99
|
+
specification_version: 4
|
113
100
|
summary: SmartProperties – Ruby accessors on steroids
|
114
101
|
test_files:
|
115
102
|
- spec/smart_properties_spec.rb
|