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 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
- 1. Add _all_ of the columns your descendant classes will need.
7
- 2. Remember that this occurrence is a telltale sign that STI is the wrong
8
- design pattern to be using...
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
- 3. Give your model a bucket. So it can hold all its extra attributes.
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 => :string
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
- * You ever intend to search against your custom attributes
147
- * You plan to instantiate tons of records at a time (the serialization overhead
148
- will hurt -- though it'll hurt less with psych)
149
- * You want to get invited to all the cool software engineer parties
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!
@@ -1,3 +1,3 @@
1
1
  module AttrBucket
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
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 a #{bucket_column.type} column, not text"
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
- - 1
9
- version: 0.1.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-27 00:00:00 -05:00
17
+ date: 2011-01-28 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency