attr_bucket 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +15 -9
- data/lib/attr_bucket/version.rb +1 -1
- data/lib/attr_bucket.rb +43 -4
- metadata +3 -3
data/README.rdoc
CHANGED
@@ -3,15 +3,16 @@
|
|
3
3
|
Sometimes you're tempted to use STI in your Rails app, but your STI classes
|
4
4
|
don't share all of the same attributes. So, you're left with two choices:
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
<b>1.</b> Add _all_ of the columns your descendant classes will need.
|
7
|
+
|
8
|
+
<b>2.</b> Remember that this occurrence is a telltale sign that STI is the wrong
|
9
|
+
design pattern to be using...
|
9
10
|
|
10
11
|
<b>OR IS IT?</b> Yes, it is. Probably.
|
11
12
|
|
12
13
|
But wait! I present you with a third option:
|
13
14
|
|
14
|
-
|
15
|
+
<b>3.</b> Give your model a bucket. So it can hold all its extra attributes.
|
15
16
|
|
16
17
|
Hoo boy. This is probably a horrible idea.
|
17
18
|
|
@@ -54,10 +55,13 @@ so we'll define our bucket with a hash, instead:
|
|
54
55
|
i_has_a_bucket :unique_traits => {
|
55
56
|
:in_possession_of_bucket => :boolean, # Has it been stolen yet?
|
56
57
|
:tusk_length_in_inches => :integer,
|
57
|
-
:also_known_as =>
|
58
|
+
:also_known_as => proc {|v| "aka #{v}"}
|
58
59
|
}
|
59
60
|
end
|
60
61
|
|
62
|
+
Note the use of the proc on the value side of the hash for :also_known_as. You can
|
63
|
+
supply any object that responds to +call+ for custom typecasting behavior.
|
64
|
+
|
61
65
|
Now, we can create some animals. Let's start with a crow.
|
62
66
|
|
63
67
|
crow = Bird.create(
|
@@ -132,6 +136,8 @@ Let's make sure the attributes got cast to the proper type:
|
|
132
136
|
=> 6
|
133
137
|
lolrus.in_possession_of_bucket
|
134
138
|
=> true
|
139
|
+
lolrus.also_known_as
|
140
|
+
=> "aka The Holder of the Bucket"
|
135
141
|
|
136
142
|
That lolrus looks to be in fine shape, indeed.
|
137
143
|
|
@@ -143,10 +149,10 @@ from an abstract class or something a bit, well, more sane.
|
|
143
149
|
|
144
150
|
attr_bucket isn't for you if:
|
145
151
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
152
|
+
* You ever intend to search against your custom attributes
|
153
|
+
* You plan to instantiate tons of records at a time (the serialization overhead
|
154
|
+
will hurt -- though it'll hurt less with psych)
|
155
|
+
* You want to get invited to all the cool software engineer parties
|
150
156
|
|
151
157
|
All that caveat-ing aside, give it a try, and let me know if you find a particularly
|
152
158
|
clever use case!
|
data/lib/attr_bucket/version.rb
CHANGED
data/lib/attr_bucket.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
module AttrBucket
|
2
|
-
def self.included(base)
|
2
|
+
def self.included(base) #:nodoc:
|
3
3
|
base.extend ClassMethods
|
4
4
|
end
|
5
5
|
|
6
6
|
private
|
7
7
|
|
8
|
+
# Retrieve the attribute bucket, or if it's not yet a Hash,
|
9
|
+
# initialize it as one.
|
8
10
|
def get_attr_bucket(name)
|
9
11
|
unless read_attribute(name).is_a?(Hash)
|
10
12
|
write_attribute(name, {})
|
@@ -12,8 +14,17 @@ module AttrBucket
|
|
12
14
|
read_attribute(name)
|
13
15
|
end
|
14
16
|
|
17
|
+
# Swipe the nifty column typecasting from the column class
|
18
|
+
# underlying the bucket column, or use the call method of
|
19
|
+
# the object supplied for +type+ if it responds to call.
|
20
|
+
#
|
21
|
+
# This allows custom typecasting by supplying a proc, etc
|
22
|
+
# as the value side of the hash in an attr_bucket definition.
|
15
23
|
def explicitly_type_cast(value, type, column_class)
|
16
24
|
return nil if value.nil?
|
25
|
+
|
26
|
+
return type.call(value) if type.respond_to?(:call)
|
27
|
+
|
17
28
|
case type
|
18
29
|
when :string then value.to_s
|
19
30
|
when :text then value.to_s
|
@@ -33,12 +44,40 @@ module AttrBucket
|
|
33
44
|
module ClassMethods
|
34
45
|
private
|
35
46
|
|
47
|
+
# Accepts an options hash of the format:
|
48
|
+
#
|
49
|
+
# :<bucket-name> => <bucket-attributes>,
|
50
|
+
# [:<bucket-name> => <bucket-attributes>], [...]
|
51
|
+
#
|
52
|
+
# where <tt><bucket-name></tt> is the +text+ column being used for
|
53
|
+
# serializing the objects, and <tt><bucket-attributes></tt> is:
|
54
|
+
#
|
55
|
+
# * A single attribute name in symbol format (not much point...)
|
56
|
+
# * An array of attribute names
|
57
|
+
# * A hash, in which the keys are the attribute names in Symbol format,
|
58
|
+
# and the values describe what they should be typecast as. The valid
|
59
|
+
# choices are +string+, +text+, +integer+, +float+, +decimal+, +datetime+,
|
60
|
+
# +timestamp+, +time+, +date+, +binary+, or +boolean+. This will invoke
|
61
|
+
# the same typecasting behavior as is normally used by the underlying
|
62
|
+
# ActiveRecord column (specific to your database).
|
63
|
+
#
|
64
|
+
# Alternately, you may specify a Proc or another object that responds to
|
65
|
+
# +call+, and it will be invoked with the value being assigned to the
|
66
|
+
# attribute as its only parameter, for custom typecasting behavior.
|
67
|
+
#
|
68
|
+
# Example:
|
69
|
+
#
|
70
|
+
# attr_bucket :bucket => {
|
71
|
+
# :is_awesome => :boolean
|
72
|
+
# :circumference => :integer,
|
73
|
+
# :flanderized_name => proc {|val| "#{val}-diddly"}
|
74
|
+
# }
|
36
75
|
def attr_bucket(opts = {})
|
37
76
|
opts.map do |bucket_name, attrs|
|
38
77
|
bucket_column = self.columns_hash[bucket_name.to_s]
|
39
78
|
unless bucket_column.type == :text
|
40
79
|
raise ArgumentError,
|
41
|
-
"#{bucket_name} is
|
80
|
+
"#{bucket_name} is of type #{bucket_column.type}, not text"
|
42
81
|
end
|
43
82
|
serialize bucket_name, Hash
|
44
83
|
|
@@ -58,13 +97,13 @@ module AttrBucket
|
|
58
97
|
|
59
98
|
alias :i_has_a_bucket :attr_bucket
|
60
99
|
|
61
|
-
def define_bucket_reader(bucket_name, attr_name)
|
100
|
+
def define_bucket_reader(bucket_name, attr_name) #:nodoc:
|
62
101
|
define_method attr_name do
|
63
102
|
get_attr_bucket(bucket_name)[attr_name]
|
64
103
|
end unless method_defined? attr_name
|
65
104
|
end
|
66
105
|
|
67
|
-
def define_bucket_writer(bucket_name, attr_name, attr_type, column_class)
|
106
|
+
def define_bucket_writer(bucket_name, attr_name, attr_type, column_class) #:nodoc:
|
68
107
|
define_method "#{attr_name}=" do |val|
|
69
108
|
# TODO: Make this more resilient/granular for multiple bucketed changes
|
70
109
|
send("#{bucket_name}_will_change!")
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 2
|
9
|
+
version: 0.1.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ernie Miller
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-28 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|