Skip to content
Browse files

Adding :values option support for Set. v1.1.0

  • Loading branch information...
1 parent c3bef01 commit d8539f95f522131add5cae95b2a18e8e47ed4598 @kenn committed Sep 20, 2012
Showing with 35 additions and 14 deletions.
  1. +15 −3 README.md
  2. +11 −5 lib/store_field.rb
  3. +1 −1 lib/store_field/version.rb
  4. +8 −5 spec/store_field_spec.rb
View
18 README.md
@@ -17,9 +17,9 @@ user.tutorials[:quick_start] = :visited # => NoMethodError: undefined method
There are two ways to solve this problem - a. break down `options` into multiple columns like `tutorials` and `preference`, or b. define an accessor method for each to initialize with an empty `Hash` when accessed for the first time.
-The former is bad because the TEXT (or BLOB) column type could be [stored off-page](http://www.mysqlperformanceblog.com/2010/02/09/blob-storage-in-innodb/) when it gets big and you could hit some strange bugs and/or performance penalty. Furthermore, adding columns kills the primary purpose of having key-value store - you use this feature because you don't like migrations, right? So it's two-fold bad.
+The former is bad because the TEXT (or BLOB) column type could be stored off-page when it gets big and you could hit some [strange bugs and/or performance penalty](http://www.mysqlperformanceblog.com/2010/02/09/blob-storage-in-innodb/). Furthermore, adding columns kills the primary purpose of having key-value store - you use this feature because you don't like migrations, right? So it's two-fold bad.
-StoreField takes the latter approach. It defines accessors that initializes with an empty `Hash` or `Set` automatically. Now you have a single TEXT column for everything!
+StoreField takes the latter approach. It defines accessors that initialize with an empty `Hash` or `Set` automatically. Now you have a single TEXT column for everything!
## Usage
@@ -78,9 +78,21 @@ cart.funnel # => #<Set: {:add_item, :checkout}>
cart.set_funnel(:checkout).save! # => true
```
+Also you can enumerate acceptable values, which is validated on the `set_[field]` method.
+
+```ruby
+store_field :funnel, type: Set, values: [ :add_item, :checkout ]
+```
+
+With the definition above, the following code will raise an exception.
+
+```ruby
+set_funnel(:bogus) # => ArgumentError: :bogus is not allowed
+```
+
## Use cases for the Set type
-Set is a great way to store an arbitrary number of states.
+Set is a great way to store an arbitrary number of named states.
Consider you have a system that sends an alert when some criteria have been met.
View
16 lib/store_field.rb
@@ -7,26 +7,32 @@ module StoreField
module ClassMethods
def store_field(key, options = {})
- raise ArgumentError.new(':in is invalid') if options[:in] and serialized_attributes[options[:in].to_s].nil?
- raise ArgumentError.new(':type is invalid') if options[:type] and ![ Hash, Set ].include?(options[:type])
+ raise ArgumentError.new(':in is invalid') if options[:in] and serialized_attributes[options[:in].to_s].nil?
+ raise ArgumentError.new(':type is invalid') if options[:type] and ![ Hash, Set ].include?(options[:type])
+ raise ArgumentError.new(':values is invalid') if options[:values] and !options[:values].is_a?(Array)
klass = options[:type]
+ values = options[:values]
store_attribute = options[:in] || serialized_attributes.keys.first
raise ArgumentError.new('store method must be defined before store_field') if store_attribute.nil?
# Accessor
define_method(key) do
- value = send("#{store_attribute}")[key]
+ value = send(store_attribute)[key]
if value.nil?
value = klass ? klass.new : {}
- send("#{store_attribute}")[key] = value
+ send(store_attribute)[key] = value
end
value
end
# Utility methods for Set
if options[:type] == Set
- define_method("set_#{key}") {|value| send(key).add(value); self }
+ define_method("set_#{key}") do |value|
+ raise ArgumentError.new("#{value.inspect} is not allowed") if values and !values.include?(value)
+ send(key).add(value)
+ self
+ end
define_method("unset_#{key}") {|value| send(key).delete(value); self }
define_method("set_#{key}?") {|value| send(key).include?(value) }
define_method("unset_#{key}?") {|value| !send(key).include?(value) }
View
2 lib/store_field/version.rb
@@ -1,3 +1,3 @@
module StoreField
- VERSION = '1.0.0'
+ VERSION = '1.1.0'
end
View
13 spec/store_field_spec.rb
@@ -7,7 +7,7 @@
class User < ActiveRecord::Base
store :storage
store_field :tutorials
- store_field :delivered, type: Set
+ store_field :delivered, type: Set, values: [ :welcome, :balance_low ]
end
describe StoreField do
@@ -30,19 +30,22 @@ class User < ActiveRecord::Base
@user.delivered.should == Set.new
end
+ it 'raises when invalid value is given for Set' do
+ expect {
+ @user.set_delivered(:bogus)
+ }.to raise_error(ArgumentError)
+ end
+
it 'sets and unsets keywords' do
@user.set_delivered(:welcome)
- @user.set_delivered(:first_deposit)
# Consume balance, notify once and only once
@user.set_delivered(:balance_low)
- @user.set_delivered(:balance_negative)
# Another deposit, restore balance
@user.unset_delivered(:balance_low)
- @user.unset_delivered(:balance_negative)
- @user.delivered.should == Set.new([:welcome, :first_deposit])
+ @user.delivered.should == Set.new([:welcome])
end
it 'saves in-line' do

0 comments on commit d8539f9

Please sign in to comment.
Something went wrong with that request. Please try again.