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 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
- # SmartProperties
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
- gem 'smart_properties'
9
+ ```ruby
10
+ gem 'smart_properties'
11
+ ```
10
12
 
11
13
  And then execute:
12
14
 
13
- $ bundle
15
+ ```plain
16
+ $ bundle
17
+ ```
14
18
 
15
19
  Or install it yourself as:
16
20
 
17
- $ gem install smart_properties
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
- class Message
51
- property :subject, :converts => :to_s
52
-
53
- property :body, :converts => :to_s
54
-
55
- property :priority, :converts => :to_sym,
56
- :accepts => [:low, :normal, :high],
57
- :default => :normal
58
- :required => true
59
- end
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
- Message.new # => raises ArgumentError, "Message requires the property subject to be set"
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
- m = Message.new :subject => 'Lorem ipsum'
72
- m.priority # => :normal
73
- m.priority = :urgent # => raises ArgumentError, Message does not accept :urgent as value for the property priority
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
- class Article
93
- property :title, :converts => :to_s
94
- end
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
- class Article
103
- property :slug, :converts => lambda { |slug| slug.downcase.gsub(/\s+/, '-').gsub(/\W/, '') }
104
- end
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
- class Article
115
- property :title, :accepts => String
116
- end
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
- class Article
123
- property :published, :accepts => [true, false]
124
- end
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
- class Article
133
- property :title, :accepts => lambda { |title| /^Lorem \w+$/ =~ title }
134
- end
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
- class Article
144
- property :id, :default => 42
145
- end
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 set and never `nil`, you can use the
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
- class Article
155
- property :title, :required => true
156
- end
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
 
@@ -22,7 +22,7 @@
22
22
  #
23
23
  module SmartProperties
24
24
 
25
- VERSION = "1.2.3"
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 = !!attrs.delete(: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=(value)
48
- @required = !!value
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
- @value = value
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.should respond_to(:property)
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
- prerelease:
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: 2012-11-27 00:00:00.000000000 Z
11
+ date: 2013-08-04 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- type: :development
16
- version_requirements: !ruby/object:Gem::Requirement
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
- name: rspec
20
+ type: :development
23
21
  prerelease: false
24
- requirement: !ruby/object:Gem::Requirement
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
- type: :development
32
- version_requirements: !ruby/object:Gem::Requirement
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
- name: rake
34
+ type: :development
39
35
  prerelease: false
40
- requirement: !ruby/object:Gem::Requirement
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
- type: :development
48
- version_requirements: !ruby/object:Gem::Requirement
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
- name: guard-rspec
48
+ type: :development
55
49
  prerelease: false
56
- requirement: !ruby/object:Gem::Requirement
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: ! " SmartProperties are a more flexible and feature-rich alternative
63
- to\n traditional Ruby accessors. They provide support for input conversion,\n input
64
- validation, specifying default values and presence checking.\n"
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: 1.8.24
97
+ rubygems_version: 2.0.6
111
98
  signing_key:
112
- specification_version: 3
99
+ specification_version: 4
113
100
  summary: SmartProperties – Ruby accessors on steroids
114
101
  test_files:
115
102
  - spec/smart_properties_spec.rb