characterizable 0.0.9 → 0.0.10
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/README.rdoc +52 -0
- data/VERSION +1 -1
- data/characterizable.gemspec +60 -60
- data/lib/characterizable.rb +31 -40
- data/test/test_characterizable.rb +51 -12
- metadata +14 -3
data/README.rdoc
CHANGED
@@ -4,6 +4,58 @@
|
|
4
4
|
|
5
5
|
http://tinyurl.com/28tcrw2
|
6
6
|
|
7
|
+
== What is a BetterHash?
|
8
|
+
|
9
|
+
It just brings some Ruby 1.9 behavior to Ruby 1.8 hashes... (from http://webonrails.com/2009/02/06/ruby-191-hash)
|
10
|
+
|
11
|
+
RUBY_VERSION => 1.8.6
|
12
|
+
>> hash = {:a=> 1, :b=>2, :c=>3, :d=>4}
|
13
|
+
=> {:b=>2, :c=>3, :d=>4, :a=>1}
|
14
|
+
>> hash.select{|k,v| k == :c }
|
15
|
+
=> [[:c, 3]]
|
16
|
+
|
17
|
+
RUBY_VERSION => 1.9.1
|
18
|
+
>> hash = {:a=> 1, :b=>2, :c=>3, :d=>4}
|
19
|
+
=> {:a=>1, :b=>2, :c=>3, :d=>4}
|
20
|
+
>> hash.select{|k,v| k == :c }
|
21
|
+
=> {:c=>3}
|
22
|
+
|
23
|
+
I hope these two tests show the value of BetterHash in the context of this gem...
|
24
|
+
|
25
|
+
should "not be annoying to work with characteristics on a particular instance" do
|
26
|
+
a = SimpleAutomobile.new
|
27
|
+
a.make = 'Nissan'
|
28
|
+
assert_same_contents [:make], a.characteristics.effective.keys
|
29
|
+
assert_same_contents [:make], a.characteristics.effective.select { true }.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
should "not be annoying to work with characteristics hashes on a class level"
|
33
|
+
assert_same_contents [:make, :model, :variant], SimpleAutomobile.characteristics.keys
|
34
|
+
assert_same_contents [:make, :model, :variant], SimpleAutomobile.characteristics.select { true }.keys
|
35
|
+
end
|
36
|
+
|
37
|
+
If you didn't have BetterHash, you wouldn't be able to call <tt>#keys</tt> because in Ruby 1.8 <tt>Hash#select</tt> (and <tt>#reject</tt>) gives you back an Array.
|
38
|
+
|
39
|
+
== What is a Snapshot?
|
40
|
+
|
41
|
+
It's a hash of the characteristics at a particular time...
|
42
|
+
|
43
|
+
should "keep snapshots separately" do
|
44
|
+
my_car = Automobile.new
|
45
|
+
my_car.make = 'Ford'
|
46
|
+
my_car.model_year = 1999
|
47
|
+
snapshot = my_car.characteristics
|
48
|
+
assert_same_contents [:make, :model_year], snapshot.effective.keys
|
49
|
+
my_car.make = nil
|
50
|
+
assert_same_contents [], my_car.characteristics.effective.keys # up to date!
|
51
|
+
assert_same_contents [:make, :model_year], snapshot.effective.keys # frozen in time!
|
52
|
+
end
|
53
|
+
|
54
|
+
There are two important points here:
|
55
|
+
|
56
|
+
* If you call <tt>my_car.characteristics</tt>, you will always get the most recent snapshot
|
57
|
+
* If you save the output to a variable like <tt>snapshot</tt>, that snapshot won't change
|
58
|
+
|
7
59
|
== Note on Patches/Pull Requests
|
8
60
|
|
9
61
|
* Fork the project.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.10
|
data/characterizable.gemspec
CHANGED
@@ -1,60 +1,60 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = %q{characterizable}
|
8
|
-
s.version = "0.0.
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Andy Rossmeissl", "Seamus Abshere"]
|
12
|
-
s.date = %q{2010-07-06}
|
13
|
-
s.description = %q{Characterize the relationship between "attributes" (getters/setters) of instances of a class}
|
14
|
-
s.email = %q{seamus@abshere.net}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE",
|
17
|
-
"README.rdoc"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
".gitignore",
|
22
|
-
"LICENSE",
|
23
|
-
"README.rdoc",
|
24
|
-
"Rakefile",
|
25
|
-
"VERSION",
|
26
|
-
"characterizable.gemspec",
|
27
|
-
"lib/characterizable.rb",
|
28
|
-
"test/helper.rb",
|
29
|
-
"test/test_characterizable.rb"
|
30
|
-
]
|
31
|
-
s.homepage = %q{http://github.com/seamusabshere/characterizable}
|
32
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
-
s.require_paths = ["lib"]
|
34
|
-
s.rubygems_version = %q{1.3.
|
35
|
-
s.summary = %q{Characterize instances of a class}
|
36
|
-
s.test_files = [
|
37
|
-
"test/helper.rb",
|
38
|
-
"test/test_characterizable.rb"
|
39
|
-
]
|
40
|
-
|
41
|
-
if s.respond_to? :specification_version then
|
42
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
43
|
-
s.specification_version = 3
|
44
|
-
|
45
|
-
if Gem::Version.new(Gem::
|
46
|
-
s.add_runtime_dependency(%q<blockenspiel>, [">= 0.3.2"])
|
47
|
-
s.add_runtime_dependency(%q<activesupport>, [">= 2.3.5"])
|
48
|
-
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
49
|
-
else
|
50
|
-
s.add_dependency(%q<blockenspiel>, [">= 0.3.2"])
|
51
|
-
s.add_dependency(%q<activesupport>, [">= 2.3.5"])
|
52
|
-
s.add_dependency(%q<shoulda>, [">= 0"])
|
53
|
-
end
|
54
|
-
else
|
55
|
-
s.add_dependency(%q<blockenspiel>, [">= 0.3.2"])
|
56
|
-
s.add_dependency(%q<activesupport>, [">= 2.3.5"])
|
57
|
-
s.add_dependency(%q<shoulda>, [">= 0"])
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{characterizable}
|
8
|
+
s.version = "0.0.10"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Andy Rossmeissl", "Seamus Abshere"]
|
12
|
+
s.date = %q{2010-07-06}
|
13
|
+
s.description = %q{Characterize the relationship between "attributes" (getters/setters) of instances of a class}
|
14
|
+
s.email = %q{seamus@abshere.net}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"characterizable.gemspec",
|
27
|
+
"lib/characterizable.rb",
|
28
|
+
"test/helper.rb",
|
29
|
+
"test/test_characterizable.rb"
|
30
|
+
]
|
31
|
+
s.homepage = %q{http://github.com/seamusabshere/characterizable}
|
32
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.rubygems_version = %q{1.3.7}
|
35
|
+
s.summary = %q{Characterize instances of a class}
|
36
|
+
s.test_files = [
|
37
|
+
"test/helper.rb",
|
38
|
+
"test/test_characterizable.rb"
|
39
|
+
]
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
43
|
+
s.specification_version = 3
|
44
|
+
|
45
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
46
|
+
s.add_runtime_dependency(%q<blockenspiel>, [">= 0.3.2"])
|
47
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.3.5"])
|
48
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
49
|
+
else
|
50
|
+
s.add_dependency(%q<blockenspiel>, [">= 0.3.2"])
|
51
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.5"])
|
52
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
53
|
+
end
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<blockenspiel>, [">= 0.3.2"])
|
56
|
+
s.add_dependency(%q<activesupport>, [">= 2.3.5"])
|
57
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
data/lib/characterizable.rb
CHANGED
@@ -3,6 +3,7 @@ require 'blockenspiel'
|
|
3
3
|
require 'active_support'
|
4
4
|
require 'active_support/version'
|
5
5
|
%w{
|
6
|
+
active_support/core_ext/hash/slice
|
6
7
|
active_support/core_ext/class/attribute_accessors
|
7
8
|
active_support/core_ext/object/blank
|
8
9
|
active_support/core_ext/array/wrap
|
@@ -26,45 +27,45 @@ module Characterizable
|
|
26
27
|
@_characteristics = nil
|
27
28
|
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
unless block.call(*ary)
|
39
|
-
memo[ary[0]] = ary[1]
|
30
|
+
class BetterHash < ::Hash
|
31
|
+
# In Ruby 1.9, running select/reject/etc. gives you back a hash
|
32
|
+
if RUBY_VERSION < '1.9'
|
33
|
+
def reject(&block)
|
34
|
+
inject(Characterizable::BetterHash.new) do |memo, ary|
|
35
|
+
unless block.call(*ary)
|
36
|
+
memo[ary[0]] = ary[1]
|
37
|
+
end
|
38
|
+
memo
|
40
39
|
end
|
41
|
-
memo
|
42
40
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
41
|
+
def select(&block)
|
42
|
+
inject(Characterizable::BetterHash.new) do |memo, ary|
|
43
|
+
if block.call(*ary)
|
44
|
+
memo[ary[0]] = ary[1]
|
45
|
+
end
|
46
|
+
memo
|
48
47
|
end
|
49
|
-
memo
|
50
48
|
end
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
49
|
+
# I need this because otherwise it will try to do self.class.new on subclasses
|
50
|
+
# which would get "0 for 1" arguments error with Snapshot, among other things
|
51
|
+
def slice(*keep)
|
52
|
+
inject(Characterizable::BetterHash.new) do |memo, ary|
|
53
|
+
if keep.include?(ary[0])
|
54
|
+
memo[ary[0]] = ary[1]
|
55
|
+
end
|
56
|
+
memo
|
56
57
|
end
|
57
|
-
memo
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
class Snapshot <
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
class Snapshot < BetterHash
|
63
|
+
attr_reader :target
|
64
|
+
def initialize(target)
|
65
|
+
@target = target
|
66
|
+
_take_snapshot
|
66
67
|
end
|
67
|
-
def
|
68
|
+
def _take_snapshot
|
68
69
|
target.characterizable_base.characteristics.each do |_, c|
|
69
70
|
if c.known?(target)
|
70
71
|
if c.effective?(target)
|
@@ -78,16 +79,6 @@ module Characterizable
|
|
78
79
|
end
|
79
80
|
end
|
80
81
|
end
|
81
|
-
def slice(*keep)
|
82
|
-
copy = self.class.new *survivor_args
|
83
|
-
copy.keys.each do |key|
|
84
|
-
copy.delete key unless keep.include? key
|
85
|
-
end
|
86
|
-
copy
|
87
|
-
end
|
88
|
-
def target
|
89
|
-
survivor_args.first
|
90
|
-
end
|
91
82
|
def []=(key, value)
|
92
83
|
target.expire_snapshot!
|
93
84
|
super
|
@@ -132,7 +123,7 @@ module Characterizable
|
|
132
123
|
@klass = klass
|
133
124
|
end
|
134
125
|
def characteristics
|
135
|
-
@_characteristics ||=
|
126
|
+
@_characteristics ||= BetterHash.new
|
136
127
|
end
|
137
128
|
include Blockenspiel::DSL
|
138
129
|
def has(name, options = {}, &block)
|
@@ -62,22 +62,35 @@ class TestCharacterizable < Test::Unit::TestCase
|
|
62
62
|
should "survive as a certain kind of hash" do
|
63
63
|
a = SimpleAutomobile.new
|
64
64
|
|
65
|
-
assert_equal Characterizable::
|
66
|
-
assert_equal Characterizable::
|
67
|
-
assert_equal Characterizable::
|
68
|
-
assert_equal Characterizable::
|
65
|
+
assert_equal Characterizable::BetterHash, SimpleAutomobile.characteristics.class
|
66
|
+
assert_equal Characterizable::BetterHash, SimpleAutomobile.characteristics.select { false }.class
|
67
|
+
assert_equal Characterizable::BetterHash, SimpleAutomobile.characteristics.slice(:hello).class
|
68
|
+
assert_equal Characterizable::BetterHash, SimpleAutomobile.characteristics.merge({:hi => 'there'}).class
|
69
69
|
|
70
|
-
assert_equal Characterizable::
|
71
|
-
assert_equal Characterizable::
|
72
|
-
assert_equal Characterizable::
|
73
|
-
assert_equal Characterizable::
|
70
|
+
assert_equal Characterizable::BetterHash, a.characteristics.effective.class
|
71
|
+
assert_equal Characterizable::BetterHash, a.characteristics.effective.select { false }.class
|
72
|
+
assert_equal Characterizable::BetterHash, a.characteristics.effective.slice(:hello).class
|
73
|
+
assert_equal Characterizable::BetterHash, a.characteristics.effective.merge({:hi => 'there'}).class
|
74
|
+
|
75
|
+
assert_equal Characterizable::BetterHash, a.characteristics.select { false }.class
|
76
|
+
assert_equal Characterizable::BetterHash, a.characteristics.slice(:hello).class
|
74
77
|
|
75
78
|
assert_equal Characterizable::Snapshot, a.characteristics.class
|
76
|
-
assert_equal Characterizable::Snapshot, a.characteristics.select { false }.class
|
77
|
-
assert_equal Characterizable::Snapshot, a.characteristics.slice(:hello).class
|
78
79
|
assert_equal Characterizable::Snapshot, a.characteristics.merge({:hi => 'there'}).class
|
79
80
|
end
|
80
81
|
|
82
|
+
should "not be annoying to work with characteristics on a particular instance" do
|
83
|
+
a = SimpleAutomobile.new
|
84
|
+
a.make = 'Nissan'
|
85
|
+
assert_same_contents [:make], a.characteristics.effective.keys
|
86
|
+
assert_same_contents [:make], a.characteristics.effective.select { true }.keys
|
87
|
+
end
|
88
|
+
|
89
|
+
should "not be annoying to work with characteristics hashes on a class level" do
|
90
|
+
assert_same_contents [:make, :model, :variant], SimpleAutomobile.characteristics.keys
|
91
|
+
assert_same_contents [:make, :model, :variant], SimpleAutomobile.characteristics.select { true }.keys
|
92
|
+
end
|
93
|
+
|
81
94
|
should "tell you what characteristics are effective" do
|
82
95
|
a = SimpleAutomobile.new
|
83
96
|
a.make = 'Ford'
|
@@ -229,8 +242,8 @@ class TestCharacterizable < Test::Unit::TestCase
|
|
229
242
|
snapshot = a.characteristics
|
230
243
|
assert_same_contents [:make, :model_year], snapshot.effective.keys
|
231
244
|
a.make = nil
|
232
|
-
assert_same_contents [], a.characteristics.effective.keys
|
233
|
-
assert_same_contents [:make, :model_year], snapshot.effective.keys
|
245
|
+
assert_same_contents [], a.characteristics.effective.keys # up to date!
|
246
|
+
assert_same_contents [:make, :model_year], snapshot.effective.keys # frozen in time!
|
234
247
|
end
|
235
248
|
|
236
249
|
should "work when passed around as a snapshot" do
|
@@ -323,5 +336,31 @@ class TestCharacterizable < Test::Unit::TestCase
|
|
323
336
|
a.make = 'Ford'
|
324
337
|
assert_equal({ :make => 'Ford' }, a.characteristics)
|
325
338
|
assert_equal({}, a.characteristics.slice(:dummy))
|
339
|
+
assert_equal({ :make => 'Ford' }, a.characteristics.slice(:make))
|
340
|
+
end
|
341
|
+
|
342
|
+
should 'allow characterizations to be selected' do
|
343
|
+
a = Automobile.new
|
344
|
+
a.make = 'Ford'
|
345
|
+
assert_equal({ :make => 'Ford' }, a.characteristics.select { |k, v| k == :make })
|
346
|
+
assert_equal({}, a.characteristics.select { |k, v| k == :dummy })
|
347
|
+
end
|
348
|
+
|
349
|
+
should 'allow characterizations to be rejected' do
|
350
|
+
a = Automobile.new
|
351
|
+
a.make = 'Ford'
|
352
|
+
assert_equal({ :make => 'Ford' }, a.characteristics.reject { |k, v| k == :dummy })
|
353
|
+
assert_equal({}, a.characteristics.reject { |k, v| k == :make })
|
326
354
|
end
|
355
|
+
|
356
|
+
should 'not reset snapshot after slicing' do
|
357
|
+
a = Automobile.new
|
358
|
+
a.make = 'Ford'
|
359
|
+
snapshot = a.characteristics.slice(:dummy)
|
360
|
+
assert_equal({}, snapshot)
|
361
|
+
a.make = 'Nissan'
|
362
|
+
assert_equal({}, snapshot)
|
363
|
+
assert_equal({}, snapshot.slice(:make))
|
364
|
+
end
|
365
|
+
|
327
366
|
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: characterizable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 11
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
9
|
+
- 10
|
10
|
+
version: 0.0.10
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Andy Rossmeissl
|
@@ -22,9 +23,11 @@ dependencies:
|
|
22
23
|
name: blockenspiel
|
23
24
|
prerelease: false
|
24
25
|
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
25
27
|
requirements:
|
26
28
|
- - ">="
|
27
29
|
- !ruby/object:Gem::Version
|
30
|
+
hash: 23
|
28
31
|
segments:
|
29
32
|
- 0
|
30
33
|
- 3
|
@@ -36,9 +39,11 @@ dependencies:
|
|
36
39
|
name: activesupport
|
37
40
|
prerelease: false
|
38
41
|
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
39
43
|
requirements:
|
40
44
|
- - ">="
|
41
45
|
- !ruby/object:Gem::Version
|
46
|
+
hash: 9
|
42
47
|
segments:
|
43
48
|
- 2
|
44
49
|
- 3
|
@@ -50,9 +55,11 @@ dependencies:
|
|
50
55
|
name: shoulda
|
51
56
|
prerelease: false
|
52
57
|
requirement: &id003 !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
53
59
|
requirements:
|
54
60
|
- - ">="
|
55
61
|
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
56
63
|
segments:
|
57
64
|
- 0
|
58
65
|
version: "0"
|
@@ -88,23 +95,27 @@ rdoc_options:
|
|
88
95
|
require_paths:
|
89
96
|
- lib
|
90
97
|
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
91
99
|
requirements:
|
92
100
|
- - ">="
|
93
101
|
- !ruby/object:Gem::Version
|
102
|
+
hash: 3
|
94
103
|
segments:
|
95
104
|
- 0
|
96
105
|
version: "0"
|
97
106
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
98
108
|
requirements:
|
99
109
|
- - ">="
|
100
110
|
- !ruby/object:Gem::Version
|
111
|
+
hash: 3
|
101
112
|
segments:
|
102
113
|
- 0
|
103
114
|
version: "0"
|
104
115
|
requirements: []
|
105
116
|
|
106
117
|
rubyforge_project:
|
107
|
-
rubygems_version: 1.3.
|
118
|
+
rubygems_version: 1.3.7
|
108
119
|
signing_key:
|
109
120
|
specification_version: 3
|
110
121
|
summary: Characterize instances of a class
|