smart_properties 1.2.1 → 1.2.2
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.
- data/lib/smart_properties.rb +34 -58
- data/smart_properties.gemspec +1 -1
- data/spec/smart_properties_spec.rb +106 -29
- data/spec/support/smart_property_matcher.rb +46 -0
- metadata +8 -6
data/lib/smart_properties.rb
CHANGED
@@ -22,7 +22,7 @@
|
|
22
22
|
#
|
23
23
|
module SmartProperties
|
24
24
|
|
25
|
-
VERSION = "1.2.
|
25
|
+
VERSION = "1.2.2"
|
26
26
|
|
27
27
|
class Property
|
28
28
|
|
@@ -115,69 +115,35 @@ module SmartProperties
|
|
115
115
|
|
116
116
|
end
|
117
117
|
|
118
|
-
|
119
|
-
# {AttributeBuilder} is a singleton object that builds and keeps track of
|
120
|
-
# annonymous class that can be used to build attribute hashes for smart
|
121
|
-
# property enabled classes.
|
122
|
-
#
|
123
|
-
class << (AttributeBuilder = Object.new)
|
118
|
+
class PropertyCollection
|
124
119
|
|
125
|
-
|
126
|
-
# Returns an attribute builder for a given klass. If the attribute builder
|
127
|
-
# does not exist yet, it is created.
|
128
|
-
#
|
129
|
-
# @return [Class]
|
130
|
-
#
|
131
|
-
def [](klass)
|
132
|
-
store.fetch(klass) { new(property_names_for(klass)) }
|
133
|
-
end
|
120
|
+
include Enumerable
|
134
121
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
#
|
141
|
-
def build_attributes_for(klass, &block)
|
142
|
-
block.nil? ? {} : self[klass].new(&block).to_hash
|
122
|
+
attr_reader :parent
|
123
|
+
|
124
|
+
def initialize(parent = nil)
|
125
|
+
@parent = parent
|
126
|
+
@collection = {}
|
143
127
|
end
|
144
128
|
|
145
|
-
|
146
|
-
|
147
|
-
#
|
148
|
-
# @return String
|
149
|
-
#
|
150
|
-
def inspect
|
151
|
-
"AttributeBuilder"
|
129
|
+
def []=(name, value)
|
130
|
+
collection[name] = value
|
152
131
|
end
|
153
|
-
alias :to_s :inspect
|
154
132
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
# @return [Class]
|
159
|
-
#
|
160
|
-
def new(fields)
|
161
|
-
Struct.new(*fields) do
|
162
|
-
def initialize(*args, &block)
|
163
|
-
super
|
164
|
-
yield(self) if block
|
165
|
-
end
|
133
|
+
def [](name)
|
134
|
+
collection_with_parent_collection[name]
|
135
|
+
end
|
166
136
|
|
167
|
-
|
168
|
-
|
169
|
-
end
|
170
|
-
end
|
137
|
+
def each(&block)
|
138
|
+
collection_with_parent_collection.each(&block)
|
171
139
|
end
|
172
140
|
|
173
|
-
|
141
|
+
protected
|
174
142
|
|
175
|
-
|
176
|
-
klass.properties.keys
|
177
|
-
end
|
143
|
+
attr_accessor :collection
|
178
144
|
|
179
|
-
def
|
180
|
-
|
145
|
+
def collection_with_parent_collection
|
146
|
+
parent.nil? ? collection : parent.collection.merge(collection)
|
181
147
|
end
|
182
148
|
|
183
149
|
end
|
@@ -196,7 +162,7 @@ module SmartProperties
|
|
196
162
|
(ancestors[1..-1].find { |klass| klass.ancestors.include?(SmartProperties) && klass != SmartProperties })
|
197
163
|
end
|
198
164
|
|
199
|
-
parent ?
|
165
|
+
parent.nil? ? PropertyCollection.new : PropertyCollection.new(parent.properties)
|
200
166
|
end
|
201
167
|
end
|
202
168
|
|
@@ -281,11 +247,21 @@ module SmartProperties
|
|
281
247
|
#
|
282
248
|
def initialize(attrs = {}, &block)
|
283
249
|
attrs ||= {}
|
284
|
-
|
250
|
+
properties = self.class.properties.each.to_a
|
251
|
+
|
252
|
+
# Assign attributes or default values
|
253
|
+
properties.each do |_, property|
|
254
|
+
value = attrs.fetch(property.name, property.default(self))
|
255
|
+
send(:"#{property.name}=", value) if value
|
256
|
+
end
|
257
|
+
|
258
|
+
# Exectue configuration block
|
259
|
+
block.call(self) if block
|
285
260
|
|
286
|
-
|
287
|
-
|
288
|
-
|
261
|
+
# Check presence of all required properties
|
262
|
+
faulty_properties = properties.select { |_, property| property.required? && send(property.name).nil? }
|
263
|
+
unless faulty_properties.empty?
|
264
|
+
raise ArgumentError, "#{self.class.name} requires the following properties to be set: #{faulty_properties.map { |_, property| property.name }.sort.join(' ')}"
|
289
265
|
end
|
290
266
|
end
|
291
267
|
|
data/smart_properties.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |gem|
|
|
19
19
|
gem.require_paths = ["lib"]
|
20
20
|
gem.version = SmartProperties::VERSION
|
21
21
|
|
22
|
-
gem.add_development_dependency "rspec", "~> 2.
|
22
|
+
gem.add_development_dependency "rspec", "~> 2.12"
|
23
23
|
gem.add_development_dependency "rake", "~> 0.8"
|
24
24
|
gem.add_development_dependency "guard-rspec", "~> 0.7"
|
25
25
|
end
|
@@ -62,8 +62,7 @@ describe SmartProperties do
|
|
62
62
|
klass
|
63
63
|
end
|
64
64
|
|
65
|
-
|
66
|
-
its(:properties) { should have_key(:title) }
|
65
|
+
it { should have_smart_property(:title) }
|
67
66
|
|
68
67
|
context "instances of this class" do
|
69
68
|
|
@@ -124,8 +123,7 @@ describe SmartProperties do
|
|
124
123
|
Class.new(superklass)
|
125
124
|
end
|
126
125
|
|
127
|
-
|
128
|
-
its(:properties) { should have_key(:title) }
|
126
|
+
it { should have_smart_property(:title) }
|
129
127
|
|
130
128
|
context "instances of this subclass" do
|
131
129
|
|
@@ -168,9 +166,8 @@ describe SmartProperties do
|
|
168
166
|
end
|
169
167
|
end
|
170
168
|
|
171
|
-
|
172
|
-
|
173
|
-
its(:properties) { should have_key(:text) }
|
169
|
+
it { should have_smart_property(:title) }
|
170
|
+
it { should have_smart_property(:text) }
|
174
171
|
|
175
172
|
context "instances of this subclass" do
|
176
173
|
|
@@ -253,9 +250,8 @@ describe SmartProperties do
|
|
253
250
|
end
|
254
251
|
end
|
255
252
|
|
256
|
-
|
257
|
-
|
258
|
-
its(:properties) { should have_key(:type) }
|
253
|
+
it { should have_smart_property(:title) }
|
254
|
+
it { should have_smart_property(:type) }
|
259
255
|
|
260
256
|
context "instances of this class" do
|
261
257
|
|
@@ -489,29 +485,16 @@ describe SmartProperties do
|
|
489
485
|
|
490
486
|
before do
|
491
487
|
klass.tap do |c|
|
492
|
-
c.send(:property, :
|
488
|
+
c.send(:property, :priority)
|
493
489
|
end
|
494
490
|
end
|
495
491
|
|
496
|
-
context "the class
|
492
|
+
context "the class" do
|
497
493
|
|
498
|
-
subject { klass
|
494
|
+
subject { klass }
|
499
495
|
|
500
|
-
it { should
|
501
|
-
it { should
|
502
|
-
|
503
|
-
end
|
504
|
-
|
505
|
-
context "the subclass's properties" do
|
506
|
-
|
507
|
-
subject { subklass.properties }
|
508
|
-
|
509
|
-
it { should have_key(:title) }
|
510
|
-
it { should have_key(:body) }
|
511
|
-
|
512
|
-
pending '(dynamically defined property is not inherited)' do
|
513
|
-
it { should have_key(:subject) }
|
514
|
-
end
|
496
|
+
it { should have_smart_property(:title) }
|
497
|
+
it { should have_smart_property(:priority) }
|
515
498
|
|
516
499
|
end
|
517
500
|
|
@@ -519,7 +502,11 @@ describe SmartProperties do
|
|
519
502
|
|
520
503
|
subject { subklass }
|
521
504
|
|
522
|
-
it
|
505
|
+
it { should have_smart_property(:title) }
|
506
|
+
it { should have_smart_property(:body) }
|
507
|
+
it { should have_smart_property(:priority) }
|
508
|
+
|
509
|
+
it "should be initializable using a block" do
|
523
510
|
configuration_instructions = lambda do |s|
|
524
511
|
s.title = "Lorem Ipsum"
|
525
512
|
s.priority = :low
|
@@ -543,4 +530,94 @@ describe SmartProperties do
|
|
543
530
|
|
544
531
|
end
|
545
532
|
|
533
|
+
context "when building a class that has a property which is not required and has a default" do
|
534
|
+
|
535
|
+
subject(:klass) do
|
536
|
+
Class.new.tap do |c|
|
537
|
+
c.send(:include, described_class)
|
538
|
+
c.send(:property, :title, :default => 'Lorem Ipsum')
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
context 'instances of that class' do
|
543
|
+
|
544
|
+
context 'when created with a set of attributes that explicitly contains nil for the title' do
|
545
|
+
|
546
|
+
subject(:instance) { klass.new :title => nil }
|
547
|
+
|
548
|
+
it "should have no title" do
|
549
|
+
instance.title.should be_nil
|
550
|
+
end
|
551
|
+
|
552
|
+
end
|
553
|
+
|
554
|
+
context 'when created without any arguments' do
|
555
|
+
|
556
|
+
subject(:instance) { klass.new }
|
557
|
+
|
558
|
+
it "should have the default title" do
|
559
|
+
instance.title.should be == 'Lorem Ipsum'
|
560
|
+
end
|
561
|
+
|
562
|
+
end
|
563
|
+
|
564
|
+
context 'when created with an empty block' do
|
565
|
+
|
566
|
+
subject(:instance) { klass.new {} }
|
567
|
+
|
568
|
+
it "should have the default title" do
|
569
|
+
instance.title.should be == 'Lorem Ipsum'
|
570
|
+
end
|
571
|
+
|
572
|
+
end
|
573
|
+
|
574
|
+
end
|
575
|
+
|
576
|
+
end
|
577
|
+
|
578
|
+
context "when building a class that has a property which is required and has no default" do
|
579
|
+
|
580
|
+
subject(:klass) do
|
581
|
+
Class.new.tap do |c|
|
582
|
+
c.send(:include, described_class)
|
583
|
+
c.send(:property, :title, :required => true)
|
584
|
+
|
585
|
+
def c.name; "Dummy"; end
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
context 'instances of that class' do
|
590
|
+
|
591
|
+
context 'when created with a set of attributes that explicitly contains nil for the title' do
|
592
|
+
|
593
|
+
subject(:instance) { klass.new :title => 'Lorem Ipsum' }
|
594
|
+
|
595
|
+
it "should have no title" do
|
596
|
+
instance.title.should be == 'Lorem Ipsum'
|
597
|
+
end
|
598
|
+
|
599
|
+
end
|
600
|
+
|
601
|
+
context 'when created with an block specifying that property' do
|
602
|
+
|
603
|
+
subject(:instance) { klass.new { |i| i.title = 'Lorem Ipsum' } }
|
604
|
+
|
605
|
+
it "should have the default title" do
|
606
|
+
instance.title.should be == 'Lorem Ipsum'
|
607
|
+
end
|
608
|
+
|
609
|
+
end
|
610
|
+
|
611
|
+
context "when created with no arguments" do
|
612
|
+
|
613
|
+
it "should raise an error stating that required properties are missing" do
|
614
|
+
expect { subject.new }.to raise_error(ArgumentError, "Dummy requires the following properties to be set: title")
|
615
|
+
end
|
616
|
+
|
617
|
+
end
|
618
|
+
|
619
|
+
end
|
620
|
+
|
621
|
+
end
|
622
|
+
|
546
623
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Matchers
|
2
|
+
class SmartPropertyMatcher
|
3
|
+
|
4
|
+
attr_reader :property_name
|
5
|
+
attr_reader :instance
|
6
|
+
|
7
|
+
def initialize(property_name)
|
8
|
+
@property_name = property_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def matches?(instance)
|
12
|
+
@instance = instance
|
13
|
+
smart_property_enabled? && is_smart_property?
|
14
|
+
end
|
15
|
+
|
16
|
+
def failure_message
|
17
|
+
return "expected #{instance.class.name} to have a property named #{property_name}" if smart_property_enabled?
|
18
|
+
return "expected #{instance.class.name} to be smart property enabled"
|
19
|
+
end
|
20
|
+
|
21
|
+
def negative_failure_message
|
22
|
+
"expected #{instance.class.name} to not have a property named #{property_name}"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def smart_property_enabled?
|
28
|
+
instance.ancestors.include?(::SmartProperties)
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_smart_property?
|
32
|
+
instance.properties[property_name].kind_of?(::SmartProperties::Property)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_smart_property(*args)
|
38
|
+
SmartPropertyMatcher.new(*args)
|
39
|
+
end
|
40
|
+
|
41
|
+
alias :have_smart_property :has_smart_property
|
42
|
+
end
|
43
|
+
|
44
|
+
RSpec.configure do |spec|
|
45
|
+
spec.include(Matchers)
|
46
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_properties
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: '2.
|
21
|
+
version: '2.12'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '2.
|
29
|
+
version: '2.12'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: rake
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +80,7 @@ files:
|
|
80
80
|
- smart_properties.gemspec
|
81
81
|
- spec/smart_properties_spec.rb
|
82
82
|
- spec/spec_helper.rb
|
83
|
+
- spec/support/smart_property_matcher.rb
|
83
84
|
homepage: ''
|
84
85
|
licenses: []
|
85
86
|
post_install_message:
|
@@ -94,7 +95,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
95
|
version: '0'
|
95
96
|
segments:
|
96
97
|
- 0
|
97
|
-
hash:
|
98
|
+
hash: 1846347248225497114
|
98
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
100
|
none: false
|
100
101
|
requirements:
|
@@ -103,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
104
|
version: '0'
|
104
105
|
segments:
|
105
106
|
- 0
|
106
|
-
hash:
|
107
|
+
hash: 1846347248225497114
|
107
108
|
requirements: []
|
108
109
|
rubyforge_project:
|
109
110
|
rubygems_version: 1.8.24
|
@@ -113,4 +114,5 @@ summary: SmartProperties – Ruby accessors on steroids
|
|
113
114
|
test_files:
|
114
115
|
- spec/smart_properties_spec.rb
|
115
116
|
- spec/spec_helper.rb
|
117
|
+
- spec/support/smart_property_matcher.rb
|
116
118
|
has_rdoc:
|