attr_bucket 0.1.1 → 0.1.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/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