characterizable 0.0.2 → 0.0.3

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
@@ -22,11 +22,12 @@ module Characterizable
22
22
  @_characteristics ||= Snapshot.new self
23
23
  end
24
24
 
25
- def dirty_characteristics!
25
+ def expire_snapshot!
26
26
  @_characteristics = nil
27
27
  end
28
28
 
29
29
  # hashes that survive as such when you select/reject/slice them
30
+ # they also keep arguments passed to them
30
31
  class SurvivorHash < Hash
31
32
  attr_reader :survivor_args
32
33
  def initialize(*survivor_args)
@@ -63,35 +64,25 @@ module Characterizable
63
64
  super
64
65
  take_snapshot
65
66
  end
66
- def snapshotted_obj
67
+ def target
67
68
  survivor_args.first
68
69
  end
69
70
  def []=(key, value)
70
- snapshotted_obj.dirty_characteristics!
71
+ target.expire_snapshot!
71
72
  super
72
73
  end
73
74
  def take_snapshot
74
- snapshotted_obj.characterizable_base.characteristics.each do |k, c|
75
- if c.known?(snapshotted_obj) and c.requited?(snapshotted_obj) and not c.trumped?(snapshotted_obj)
76
- self[k] = snapshotted_obj.send c.name
75
+ target.characterizable_base.characteristics.each do |_, c|
76
+ if c.relevant?(target)
77
+ self[c.name] = c.value(target)
77
78
  end
78
79
  end
79
80
  end
80
- def known
81
- snapshotted_obj.characterizable_base.characteristics.select do |_, c|
82
- c.known?(self) and c.requited?(self) and not c.trumped?(self)
83
- end
84
- end
85
- def unknown
86
- snapshotted_obj.characterizable_base.characteristics.select do |_, c|
87
- c.unknown?(self) and c.requited?(self) and not c.trumped?(self)
88
- end
89
- end
90
- def visible_known
91
- known.reject { |_, c| c.hidden? }
81
+ def relevant
82
+ target.characterizable_base.characteristics.select { |_, c| c.relevant?(self) }
92
83
  end
93
- def visible_unknown
94
- unknown.reject { |_, c| c.hidden? }
84
+ def irrelevant
85
+ target.characterizable_base.characteristics.select { |_, c| c.irrelevant?(self) }
95
86
  end
96
87
  end
97
88
 
@@ -115,57 +106,55 @@ module Characterizable
115
106
  def has(name, options = {}, &block)
116
107
  characteristics[name] = Characteristic.new(self, name, options, &block)
117
108
  klass.module_eval(%{
118
- def #{name}_with_dirty_characteristics=(new_#{name})
119
- dirty_characteristics!
120
- self.#{name}_without_dirty_characteristics = new_#{name}
109
+ def #{name}_with_expire_snapshot=(new_#{name})
110
+ expire_snapshot!
111
+ self.#{name}_without_expire_snapshot = new_#{name}
121
112
  end
122
- alias_method_chain :#{name}=, :dirty_characteristics
113
+ alias_method_chain :#{name}=, :expire_snapshot
123
114
  }, __FILE__, __LINE__) if klass.instance_methods.include?("#{name}=")
124
115
  end
125
116
  end
126
-
127
117
  class Characteristic
128
118
  attr_reader :base
129
119
  attr_reader :name
130
120
  attr_reader :trumps
131
121
  attr_reader :prerequisite
132
- attr_reader :hidden
133
122
  attr_reader :options
134
123
  def initialize(base, name, options = {}, &block)
135
124
  @base = base
136
125
  @name = name
137
- @trumps = Array.wrap(options.delete(:trumps))
138
- @prerequisite = options.delete :prerequisite
139
- @hidden = options.delete :hidden
126
+ @trumps = Array.wrap options.delete(:trumps)
127
+ @prerequisite = options.delete(:prerequisite)
140
128
  @options = options
141
129
  Blockenspiel.invoke block, self if block_given?
142
130
  end
131
+ def trumped_by
132
+ @_trumped_by ||= characteristics.select { |_, c| c.trumps.include? name }
133
+ end
143
134
  delegate :characteristics, :to => :base
144
- def value(obj)
145
- case obj
135
+ def value(target)
136
+ case target
146
137
  when Hash
147
- obj[name]
138
+ target[name]
148
139
  else
149
- obj.send name
140
+ target.send name if target.respond_to?(name)
150
141
  end
151
142
  end
152
- def unknown?(obj)
153
- value(obj).nil?
143
+ def irrelevant?(target)
144
+ value(target).nil? and revealed? target and untrumped? target
154
145
  end
155
- def known?(obj)
156
- not unknown?(obj)
146
+ def relevant?(target)
147
+ !value(target).nil? and revealed? target and untrumped? target
157
148
  end
158
- def trumped?(obj)
159
- characteristics.any? do |_, c|
160
- c.known?(obj) and c.trumps.include?(name)
149
+ def untrumped?(target)
150
+ return true if trumped_by.empty?
151
+ trumped_by.none? do |_, c|
152
+ c.relevant? target
161
153
  end
162
154
  end
163
- def requited?(obj)
155
+ def revealed?(target)
164
156
  return true if prerequisite.nil?
165
- characteristics[prerequisite].known? obj
166
- end
167
- def hidden?
168
- hidden
157
+ characteristics[prerequisite].relevant? target
169
158
  end
170
159
  include Blockenspiel::DSL
171
160
  def reveals(other_name, other_options = {}, &block)
data/test/helper.rb CHANGED
@@ -2,10 +2,14 @@ require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
4
  require 'ruby-debug'
5
+ require 'set'
5
6
 
6
7
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
8
  $LOAD_PATH.unshift(File.dirname(__FILE__))
8
9
  require 'characterizable'
9
10
 
10
11
  class Test::Unit::TestCase
12
+ def assert_same_contents(a, b)
13
+ assert_equal Set.new(a), Set.new(b)
14
+ end
11
15
  end
@@ -1,6 +1,11 @@
1
1
  require 'helper'
2
2
 
3
- # TODO
3
+ class Characterizable::Characteristic
4
+ def hidden?
5
+ !!options[:hidden]
6
+ end
7
+ end
8
+
4
9
  class Automobile
5
10
  attr_accessor :make, :model_year, :model, :variant, :size_class, :hybridity, :daily_distance_estimate
6
11
  attr_accessor :record_creation_date
@@ -20,6 +25,7 @@ class Automobile
20
25
  # has :urbanity, :measures => :percentage
21
26
  has :hybridity
22
27
  has :daily_distance_estimate, :trumps => [:weekly_distance_estimate, :annual_distance_estimate, :daily_duration], :measures => :length #, :weekly_fuel_cost, :annual_fuel_cost]
28
+ has :daily_distance_oracle_estimate, :trumps => :daily_distance_estimate
23
29
  # has :daily_duration, :trumps => [:annual_distance_estimate, :weekly_distance_estimate, :daily_distance_estimate], :measures => :time #, :weekly_fuel_cost, :annual_fuel_cost]
24
30
  # has :weekly_distance_estimate, :trumps => [:annual_distance_estimate, :daily_distance_estimate, :daily_duration], :measures => :length #, :weekly_fuel_cost, :annual_fuel_cost]
25
31
  # has :annual_distance_estimate, :trumps => [:weekly_distance_estimate, :daily_distance_estimate, :daily_duration], :measures => :length #, :weekly_fuel_cost, :annual_fuel_cost]
@@ -61,10 +67,10 @@ class TestCharacterizable < Test::Unit::TestCase
61
67
  assert_equal Characterizable::SurvivorHash, SimpleAutomobile.characteristics.slice(:hello).class
62
68
  assert_equal Characterizable::SurvivorHash, SimpleAutomobile.characteristics.merge({:hi => 'there'}).class
63
69
 
64
- assert_equal Characterizable::SurvivorHash, a.characteristics.known.class
65
- assert_equal Characterizable::SurvivorHash, a.characteristics.known.select { false }.class
66
- assert_equal Characterizable::SurvivorHash, a.characteristics.known.slice(:hello).class
67
- assert_equal Characterizable::SurvivorHash, a.characteristics.known.merge({:hi => 'there'}).class
70
+ assert_equal Characterizable::SurvivorHash, a.characteristics.relevant.class
71
+ assert_equal Characterizable::SurvivorHash, a.characteristics.relevant.select { false }.class
72
+ assert_equal Characterizable::SurvivorHash, a.characteristics.relevant.slice(:hello).class
73
+ assert_equal Characterizable::SurvivorHash, a.characteristics.relevant.merge({:hi => 'there'}).class
68
74
 
69
75
  assert_equal Characterizable::Snapshot, a.characteristics.class
70
76
  assert_equal Characterizable::Snapshot, a.characteristics.select { false }.class
@@ -72,47 +78,47 @@ class TestCharacterizable < Test::Unit::TestCase
72
78
  assert_equal Characterizable::Snapshot, a.characteristics.merge({:hi => 'there'}).class
73
79
  end
74
80
 
75
- should "tell you what characteristics are known" do
81
+ should "tell you what characteristics are relevant" do
76
82
  a = SimpleAutomobile.new
77
83
  a.make = 'Ford'
78
- assert_equal [:make], a.characteristics.known.keys
84
+ assert_same_contents [:make], a.characteristics.relevant.keys
79
85
  end
80
86
 
81
- should "tell you what characteristics are unknown" do
87
+ should "tell you what characteristics are irrelevant" do
82
88
  a = SimpleAutomobile.new
83
89
  a.make = 'Ford'
84
- assert_equal [:model, :variant], a.characteristics.unknown.keys
90
+ assert_same_contents [:model, :variant], a.characteristics.irrelevant.keys
85
91
  end
86
92
 
87
- should "present a concise set of known characteristics by getting rid of those that have been trumped" do
93
+ should "present a concise set of relevant characteristics by getting rid of those that have been trumped" do
88
94
  a = SimpleAutomobile.new
89
95
  a.make = 'Ford'
90
96
  a.model = 'Taurus'
91
97
  a.variant = 'Taurus V6 DOHC'
92
- assert_equal [:make, :variant], a.characteristics.known.keys
98
+ assert_same_contents [:make, :variant], a.characteristics.relevant.keys
93
99
  end
94
100
 
95
- should "not mention a characteristic as unknown if, in fact, it has been trumped" do
101
+ should "not mention a characteristic as irrelevant if, in fact, it has been trumped" do
96
102
  a = SimpleAutomobile.new
97
103
  a.make = 'Ford'
98
104
  a.variant = 'Taurus V6 DOHC'
99
- assert_equal [], a.characteristics.unknown.keys
105
+ assert_same_contents [], a.characteristics.irrelevant.keys
100
106
  end
101
107
 
102
- should "not mention a characteristic as unknown if it is waiting on something else to be revealed" do
108
+ should "not mention a characteristic as irrelevant if it is waiting on something else to be revealed" do
103
109
  a = Automobile.new
104
- assert !a.characteristics.unknown.keys.include?(:model_year)
110
+ assert !a.characteristics.irrelevant.keys.include?(:model_year)
105
111
  end
106
112
 
107
113
  should "make sure that trumping works even within revealed characteristics" do
108
114
  a = Automobile.new
109
- assert a.characteristics.unknown.keys.include?(:size_class)
115
+ assert a.characteristics.irrelevant.keys.include?(:size_class)
110
116
  a.make = 'Ford'
111
117
  a.model_year = 1999
112
118
  a.model = 'Taurus'
113
119
  a.size_class = 'mid-size'
114
- assert_equal [:make, :model_year, :model], a.characteristics.known.keys
115
- assert !a.characteristics.unknown.keys.include?(:size_class)
120
+ assert_same_contents [:make, :model_year, :model], a.characteristics.relevant.keys
121
+ assert !a.characteristics.irrelevant.keys.include?(:size_class)
116
122
  end
117
123
 
118
124
  should "not enforce prerequisites by using an object's setter" do
@@ -121,7 +127,7 @@ class TestCharacterizable < Test::Unit::TestCase
121
127
  a.model_year = 1999
122
128
  a.make = nil
123
129
  assert_equal 1999, a.model_year
124
- assert_equal nil, a.characteristics.known[:model_year]
130
+ assert_equal nil, a.characteristics.relevant[:model_year]
125
131
  end
126
132
 
127
133
  should "keep user-defined options on a characteristic" do
@@ -132,13 +138,9 @@ class TestCharacterizable < Test::Unit::TestCase
132
138
  assert !Automobile.characteristics[:daily_distance_estimate].options.has_key?(:trumps)
133
139
  end
134
140
 
135
- should "know which characteristics are 'visible'" do
136
- a = Automobile.new
137
- assert a.characteristics.unknown.keys.include?(:record_creation_date)
138
- assert !a.characteristics.visible_unknown.keys.include?(:record_creation_date)
139
- a.record_creation_date = 'yesterday'
140
- assert a.characteristics.known.keys.include?(:record_creation_date)
141
- assert !a.characteristics.visible_known.keys.include?(:record_creation_date)
141
+ should "allow the user to add custom functionality to characteristics" do
142
+ assert Automobile.characteristics[:record_creation_date].hidden?
143
+ assert !Automobile.characteristics[:daily_distance_estimate].hidden?
142
144
  end
143
145
 
144
146
  should "be able to access values" do
@@ -147,32 +149,32 @@ class TestCharacterizable < Test::Unit::TestCase
147
149
  assert_equal 'Ford', a.characteristics[:make]
148
150
  end
149
151
 
150
- should "know what is known on a snapshot" do
152
+ should "know what is relevant on a snapshot" do
151
153
  a = Automobile.new
152
154
  a.make = 'Ford'
153
- assert_equal [:make], a.characteristics.known.keys
155
+ assert_same_contents [:make], a.characteristics.relevant.keys
154
156
  end
155
157
 
156
- should "know what is unknown on a snapshot" do
158
+ should "know what is irrelevant on a snapshot" do
157
159
  a = Automobile.new
158
160
  a.make = 'Ford'
159
- assert a.characteristics.unknown.keys.include?(:model_year)
161
+ assert a.characteristics.irrelevant.keys.include?(:model_year)
160
162
  end
161
163
 
162
- should "not reveal unknown characteristics in snapshots" do
164
+ should "not reveal irrelevant characteristics in snapshots" do
163
165
  a = Automobile.new
164
166
  a.model_year = 1999
165
- assert_equal [], a.characteristics.known.keys
167
+ assert_same_contents [], a.characteristics.relevant.keys
166
168
  assert_equal nil, a.characteristics[:model_year]
167
169
  end
168
170
 
169
- should "not reveal unknown characteristics in snapshots, even if it was previously revealed" do
171
+ should "not reveal irrelevant characteristics in snapshots, even if it was previously revealed" do
170
172
  a = Automobile.new
171
173
  a.make = 'Ford'
172
174
  a.model_year = 1999
173
- assert_equal [:make, :model_year], a.characteristics.known.keys
175
+ assert_same_contents [:make, :model_year], a.characteristics.relevant.keys
174
176
  a.make = nil
175
- assert_equal [], a.characteristics.known.keys
177
+ assert_same_contents [], a.characteristics.relevant.keys
176
178
  end
177
179
 
178
180
  should "keep snapshots separately" do
@@ -180,24 +182,81 @@ class TestCharacterizable < Test::Unit::TestCase
180
182
  a.make = 'Ford'
181
183
  a.model_year = 1999
182
184
  snapshot = a.characteristics
183
- assert_equal [:make, :model_year], snapshot.known.keys
185
+ assert_same_contents [:make, :model_year], snapshot.relevant.keys
184
186
  a.make = nil
185
- assert_equal [], a.characteristics.known.keys
186
- assert_equal [:make, :model_year], snapshot.known.keys
187
+ assert_same_contents [], a.characteristics.relevant.keys
188
+ assert_same_contents [:make, :model_year], snapshot.relevant.keys
187
189
  end
188
190
 
189
191
  should "work when passed around as a snapshot" do
190
192
  a = Automobile.new
191
193
  a.make = 'Ford'
192
194
  snapshot = a.characteristics
193
- assert_equal [:make], snapshot.known.keys
194
- assert snapshot.unknown.keys.include?(:model_year)
195
+ assert_same_contents [:make], snapshot.relevant.keys
196
+ assert snapshot.irrelevant.keys.include?(:model_year)
195
197
  snapshot[:model_year] = 1999
196
198
  assert_equal 1999, snapshot[:model_year]
197
- assert_equal [:make, :model_year], snapshot.known.keys
198
- assert !snapshot.unknown.keys.include?(:model_year)
199
+ assert_same_contents [:make, :model_year], snapshot.relevant.keys
200
+ assert !snapshot.irrelevant.keys.include?(:model_year)
199
201
  assert_equal nil, a.model_year
200
202
  assert_equal nil, a.characteristics[:model_year]
201
203
  assert_equal 1999, snapshot[:model_year]
202
204
  end
205
+
206
+ should "appreciate that sometimes characteristics just magically appear" do
207
+ a = Automobile.new
208
+ a.daily_distance_estimate = 15
209
+ snapshot = a.characteristics
210
+ snapshot[:daily_distance_oracle_estimate] = 20
211
+ assert_same_contents [:daily_distance_oracle_estimate], snapshot.relevant.keys
212
+ assert_same_contents [:daily_distance_estimate], a.characteristics.relevant.keys
213
+ end
214
+
215
+ # has :make do |make|
216
+ # make.reveals :model_year do |model_year|
217
+ # model_year.reveals :model, :trumps => :size_class do |model|
218
+ # model.reveals :variant, :trumps => :hybridity
219
+ should "handle revelations on multiple levels" do
220
+ a = Automobile.new
221
+ a.make = 'Ford'
222
+ a.model_year = 1999
223
+ a.model = 'Taurus'
224
+ a.variant = 'Taurus 1999'
225
+ assert_same_contents [:make, :model_year, :model, :variant], a.characteristics.relevant.keys
226
+ a.make = nil
227
+ assert_same_contents [], a.characteristics.relevant.keys
228
+ a.make = 'Ford'
229
+ assert_same_contents [:make, :model_year, :model, :variant], a.characteristics.relevant.keys
230
+ a.model_year = nil
231
+ assert_same_contents [:make], a.characteristics.relevant.keys
232
+ a.model_year = 1999
233
+ assert_same_contents [:make, :model_year, :model, :variant], a.characteristics.relevant.keys
234
+ a.model = nil
235
+ assert_same_contents [:make, :model_year], a.characteristics.relevant.keys
236
+ a.model = 'Taurus'
237
+ assert_same_contents [:make, :model_year, :model, :variant], a.characteristics.relevant.keys
238
+ a.variant = nil
239
+ assert_same_contents [:make, :model_year, :model], a.characteristics.relevant.keys
240
+ end
241
+
242
+ should "handle trumping on multiple levels" do
243
+ a = Automobile.new
244
+ a.size_class = 'small' # can be trumped by model
245
+ a.hybridity = 'no' # can be trumped by variant
246
+ a.make = 'Ford'
247
+ a.model_year = 1999
248
+ a.model = 'Taurus'
249
+ a.variant = 'Taurus 1999'
250
+ assert_same_contents [:make, :model_year, :model, :variant], a.characteristics.relevant.keys
251
+ a.variant = nil
252
+ assert_same_contents [:make, :model_year, :model, :hybridity], a.characteristics.relevant.keys
253
+ a.variant = 'Taurus 1999'
254
+ assert_same_contents [:make, :model_year, :model, :variant], a.characteristics.relevant.keys
255
+ a.model = nil # which reveals size class, but also hybridity!
256
+ assert_same_contents [:make, :model_year, :size_class, :hybridity], a.characteristics.relevant.keys
257
+ a.model = 'Taurus'
258
+ assert_same_contents [:make, :model_year, :model, :variant], a.characteristics.relevant.keys
259
+ a.make = nil
260
+ assert_same_contents [:size_class, :hybridity], a.characteristics.relevant.keys
261
+ end
203
262
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 2
9
- version: 0.0.2
8
+ - 3
9
+ version: 0.0.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Andy Rossmeissl
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-05-11 00:00:00 -04:00
18
+ date: 2010-05-12 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency