smart_properties 1.2.3 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|