store_configurable 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ debug.log
2
+ Gemfile.lock
3
+ *.gem
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.3
4
+ - ree
data/CHANGELOG ADDED
@@ -0,0 +1,6 @@
1
+
2
+ = 3.2.0
3
+
4
+ * Initial release. Works with ActiveRecord 3.2.x
5
+
6
+
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2008-2011 Ken Collins
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
data/README.md ADDED
@@ -0,0 +1,123 @@
1
+
2
+ # StoreConfigurable
3
+
4
+ A zero-configuration recursive Hash for storing a tree of options in a serialized ActiveRecord column. Includes self aware hooks that delegate dirty/changed state to your configs owner.
5
+
6
+ <img src="http://cdn.actionmoniker.com/share/recursive_kitty_small.jpg" alt="Recursive Github Kitty" width="260" height="160" style="float:right; margin:-20px 35px 15px 15px; background-color:#fff; padding:13px; -moz-box-shadow: 5px 5px 5px rgba(0,0,0,0.5); -webkit-box-shadow: 5px 5px 5px rgba(0,0,0,0.5); box-shadow: 5px 5px 5px rgba(0,0,0,0.5); -moz-transform: rotate(-2deg); -webkit-transform: rotate(-2deg); transform: rotate(-2deg);">
7
+
8
+ [![Build Status](https://secure.travis-ci.org/metaskills/store_configurable.png)](http://travis-ci.org/metaskills/store_configurable)
9
+
10
+ ## Installation
11
+
12
+ Install the gem with bundler. We follow a semantic versioning format that tracks ActiveRecord's minor version. So this means to use the latest 3.2.x version of StoreConfigurable with any ActiveRecord 3.2 version.
13
+
14
+ ```ruby
15
+ gem 'store_configurable', '~> 3.2.0'
16
+ ```
17
+
18
+
19
+ ## Setup
20
+
21
+ To use StoreConfigurable, you must create create a `_config` colun in the mdoel's table. Make sure that you declare this column as a text type, so there's plenty of room.
22
+
23
+ ```ruby
24
+ class AddStoreConfigurableField < ActiveRecord::Migration
25
+ def up
26
+ add_column :users, :_config, :text
27
+ end
28
+ def down
29
+ remove_column :users, :_config
30
+ end
31
+ end
32
+ ```
33
+
34
+ Next declare that your model uses StoreConfigurable with the `store_configurable` method.
35
+
36
+ ```ruby
37
+ class User < ActiveRecord::Base
38
+ store_configurable
39
+ end
40
+ ```
41
+
42
+
43
+ ## Usage
44
+
45
+ Our `config` method is your gateway to StoreConfigurable and unlike ActiveRecord's new Store object in 3.2, there is no configuration needed to start using it for any property. It will dynamically expand for every property or namespace. This allows you or other plugins' configurations to be grouped in logical nodes. All examples below assume that StoreConfigurable is being used on a User instance as shown in the setup above.
46
+
47
+ ```ruby
48
+ @user.config.remember_me = true
49
+ @user.config.sortable_tables.column = 'created_at'
50
+ @user.config.sortable_tables.direction = 'asc'
51
+ @user.config.you.should.never.need.to.do.this = 'deep_value'
52
+ @user.save
53
+ ```
54
+
55
+ #### Dirty Hooks
56
+
57
+ StoreConfigurable is smart enought to let your parent object know when it changes. It is not dumb either. It will only trigger changes if the values you set are different, are new, or change the configs state. Some examples assuming the saved record's data above.
58
+
59
+ ```ruby
60
+ @user = User.find(42)
61
+ @user.config_changed? # => false
62
+
63
+ @user.config.remember_me = true # Same value
64
+ @user.config_changed? # => false
65
+
66
+ @user.config.sortable_tables.column = 'updated_at'
67
+ @user.config.sortable_tables.direction = 'desc'
68
+ @user.config_changed? # => true
69
+ ```
70
+
71
+ #### Hash Syntax
72
+
73
+ The StoreConfigurable data objects supports most `Hash` methods with the exception of a few that rely on making a copy of the data, like `dup`. This means you can delete whole branches of data or itterate over your data collection. Again, StoreConfigurable reports all changes to the owner object via ActiveRecord's dirty support.
74
+
75
+ ```ruby
76
+ @user.config.sortable_tables.delete # Deletes this node/namespace.
77
+ @user.config.clear # Hash method to purge.
78
+ @user.config_changed? # => true
79
+ ```
80
+
81
+ #### Choose Your Style
82
+
83
+ You can choose to get or set config values via any method or hash key syntax you choose. It really does not matter! This means you can mix and match dot property notation, hash string or symbol syntax and it will just work.
84
+
85
+ ```ruby
86
+ @user.config.color = '#c1c1c1'
87
+ @user.config['remember_me'] = true
88
+ @user.config[:sortable_tables].direction = 'asc'
89
+ @user.config.sortable_tables['column'] = 'updated_at'
90
+
91
+ @user.config['color'] # => '#c1c1c1'
92
+ @user.config[:color] # => '#c1c1c1'
93
+ @user.config.remember_me # => true
94
+ @user.config.sortable_tables[:direction] # => 'asc'
95
+ @user.config[:sortable_tables][:column] # => 'updated_at'
96
+ ```
97
+
98
+
99
+ ## Stored Data
100
+
101
+ StoreConfigurable persists your configuration data in YAML format to the `_config` text column. We use Ruby's `YAML::Omap` type on the backend so we can decouple our datastore from our proxy object manager. This means you can easily load this data via other means if you want to.
102
+
103
+ ```yaml
104
+ --- !omap
105
+ - :remember_me: true
106
+ - :sortable_tables: !omap
107
+ - :column: created_at
108
+ - :direction: asc
109
+ - :you: !omap
110
+ - :should: !omap
111
+ - :never: !omap
112
+ - :need: !omap
113
+ - :to: !omap
114
+ - :do: !omap
115
+ - :this: deep_value
116
+ ```
117
+
118
+
119
+ ## License
120
+
121
+ * Released under the MIT license thanks to Decisiv, Inc.
122
+ * Copyright (c) 2011 Ken Collins
123
+
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ require 'rake/testtask'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ desc 'Test the StoreConfigurable gem.'
7
+ Rake::TestTask.new do |t|
8
+ t.libs = ['lib','test']
9
+ t.test_files = Dir.glob("test/**/*_test.rb").sort
10
+ t.verbose = true
11
+ end
12
+
13
+ task :default => [:test]
@@ -0,0 +1,38 @@
1
+ require 'active_support/concern'
2
+
3
+ module StoreConfigurable
4
+ module Base
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+
10
+ # To use StoreConfigurable, you must create create a +_config+ colun in the mdoel's table.
11
+ # Make sure that you declare this column as a text type, so there's plenty of room.
12
+ #
13
+ # class AddStoreConfigurableField < ActiveRecord::Migration
14
+ # def up
15
+ # add_column :users, :_config, :text
16
+ # end
17
+ # def down
18
+ # remove_column :users, :_config
19
+ # end
20
+ # end
21
+ #
22
+ # Next declare that your model uses StoreConfigurable with the +store_configurable+ method.
23
+ # Please read the +config+ documentation for usage examples.
24
+ #
25
+ # class User < ActiveRecord::Base
26
+ # store_configurable
27
+ # end
28
+ def store_configurable
29
+ serialize '_config', StoreConfigurable::Object
30
+ include Read
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
37
+
38
+ ActiveSupport.on_load(:active_record) { include StoreConfigurable::Base }
@@ -0,0 +1,79 @@
1
+ require 'active_support/ordered_options'
2
+
3
+ module StoreConfigurable
4
+
5
+ # The heart of StoreConfigurable's data store is this subclass of ActiveSupport's OrderedOptions.
6
+ # They are the heart of Rails' configurations and allow you to dynamically set and get hash keys
7
+ # and values using dot property notation vs the +[]+ hash accessors.
8
+ #
9
+ # However, instances of DirtyTrackingOrderedOptions use a recursive lambda via Hash's block
10
+ # initialization syntax so that you get a dynamic and endless scope on config data. Instances of
11
+ # DirtyTrackingOrderedOptions also make sure that every sub instance of it self also has a handle
12
+ # back to your store's owner. In this way when config attributes are added or values change,
13
+ # we can mark your ActiveRecord object as dirty/changed.
14
+ class DirtyTrackingOrderedOptions < ::ActiveSupport::OrderedOptions
15
+
16
+ Recursive = lambda { |h,k| h[k] = h.class.new(h.__store_configurable_owner__) }
17
+
18
+ attr_accessor :__store_configurable_owner__
19
+
20
+ def initialize(owner)
21
+ @__store_configurable_owner__ = owner
22
+ super(&Recursive)
23
+ end
24
+
25
+ def []=(key, value)
26
+ _config_may_change!(key, value)
27
+ super
28
+ end
29
+
30
+ def delete(key)
31
+ name = key.to_sym
32
+ _config_will_change! if has_key?(name)
33
+ super
34
+ end
35
+
36
+ def delete_if
37
+ _with_config_keys_may_change! { super }
38
+ end
39
+
40
+ def dup
41
+ raise NotImplementedError, 'the StoreConfigurable::Object does not support making a copy'
42
+ end
43
+ alias_method :reject, :dup
44
+
45
+ def merge(other)
46
+ dup
47
+ end
48
+
49
+ def reject!
50
+ _with_config_keys_may_change! { super }
51
+ end
52
+
53
+ def clear
54
+ _config_will_change!
55
+ super
56
+ end
57
+
58
+
59
+ protected
60
+
61
+ def _with_config_keys_may_change!
62
+ starting_keys = keys.dup
63
+ yield
64
+ _config_will_change! if starting_keys != keys
65
+ self
66
+ end
67
+
68
+ def _config_may_change!(key, value)
69
+ name = key.to_sym
70
+ _config_will_change! unless has_key?(name) && self[name] == value
71
+ end
72
+
73
+ def _config_will_change!
74
+ __store_configurable_owner__._config_will_change!
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,79 @@
1
+ require 'active_support/basic_object'
2
+
3
+ module StoreConfigurable
4
+
5
+ # The is the object returned by the +config+ method. It does nothing more than delegate
6
+ # all calls to a tree of +DirtyTrackingOrderedOptions+ objects which are basically hashes.
7
+ class Object < ::ActiveSupport::BasicObject
8
+
9
+ # Class methods so the +StoreConfigurable::Object+ responds to +dump+ and +load+ which
10
+ # allows it to conform to ActiveRecord's coder requirement via its serialize method
11
+ # that we use.
12
+ #
13
+ # The +dump+ method serializes the raw data behind the +StoreConfigurable::Object+ proxy
14
+ # object. This means that we only store pure ruby primitives in the datbase, not our
15
+ # proxy object's YAML type.
16
+ #
17
+ # The +load+ method mimics +ActiveRecord::Coders::YAMLColumn+ internals by retuning a
18
+ # new object when needed as well as making sure that the YAML we are process if of the
19
+ # same type. When reconstituting a +StoreConfigurable::Object+ we must set the store's
20
+ # owner as this does. That way as our recursive lambda loader regenerates the tree of
21
+ # config data, we always have a handle for each +DirtyTrackingOrderedOptions+ object to
22
+ # report state changes back to the owner. Finally, after each load we make sure to clear
23
+ # out changes so reloaded objects are not marked as dirty.
24
+ module Coding
25
+
26
+ def dump(value)
27
+ YAML.dump value.__config__
28
+ end
29
+
30
+ def load(yaml, owner)
31
+ return StoreConfigurable::Object.new if yaml.blank?
32
+ return yaml unless yaml.is_a?(String) && yaml =~ /^---/
33
+ stored_data = YAML.load(yaml)
34
+ unless stored_data.is_a?(Hash)
35
+ raise ActiveRecord::SerializationTypeMismatch,
36
+ "Attribute was supposed to be a Hash, but was a #{stored_data.class}"
37
+ end
38
+ config = StoreConfigurable::Object.new
39
+ config.__store_configurable_owner__ = owner
40
+ loader = lambda do |options, key, value|
41
+ value.is_a?(Hash) ? value.each { |k,v| loader.call(options.send(key), k, v) } :
42
+ options.send("#{key}=", value)
43
+ end
44
+ stored_data.each { |k,v| loader.call(config, k, v) }
45
+ owner.changed_attributes.delete('_config')
46
+ config
47
+ end
48
+
49
+ end
50
+
51
+ # Instance methods for +StoreConfigurable::Object+ defined and included in a module so
52
+ # that if you ever wanted to, you could redefine these methods and +super+ up.
53
+ module Behavior
54
+
55
+ attr_accessor :__store_configurable_owner__
56
+
57
+ def __config__
58
+ @__config__ ||= DirtyTrackingOrderedOptions.new(@__store_configurable_owner__)
59
+ end
60
+
61
+ def inspect
62
+ "#<StoreConfigurable::Object:#{object_id}>"
63
+ end
64
+
65
+ private
66
+
67
+ def method_missing(method, *args, &block)
68
+ __config__.__send__ method, *args, &block
69
+ end
70
+
71
+ end
72
+
73
+ extend Coding
74
+ include Behavior
75
+
76
+ end
77
+
78
+ end
79
+
@@ -0,0 +1,54 @@
1
+ module StoreConfigurable
2
+ module Read
3
+
4
+ # Our main syntatic interface to the underlying +_config+ store. This method ensures that
5
+ # +self+, the store's owner, will allways be set in the config object. Hence allowing all
6
+ # other recursive options to get a handle back to the owner.
7
+ #
8
+ # The config object can be treated as a Hash but in actuality is an enhanced subclass of
9
+ # +ActiveSupport::OrderedOptions+ that does two important things. First, it allows you to
10
+ # dynamically define any nested namespace property. Second, it hooks back into your parent
11
+ # object to notify it of change via ActiveRecord's dirty support.
12
+ #
13
+ # Example:
14
+ #
15
+ # class User < ActiveRecord::Base
16
+ # store_configurable
17
+ # end
18
+ #
19
+ # user = User.find(42)
20
+ # user.config.remember_me = true
21
+ # user.config.sortable_tables.products.sort_on = 'created_at'
22
+ # user.config.sortable_tables.products.direction = 'asc'
23
+ # user.changed? # => true
24
+ # user.config_changed? # => true
25
+ def config
26
+ _config.__store_configurable_owner__ = self
27
+ _config
28
+ end
29
+
30
+ # Simple delegation to the underlying data attribute's changed query method.
31
+ def config_changed?
32
+ _config_changed?
33
+ end
34
+
35
+ # Simple delegation to the underlying data attribute's change array.
36
+ def config_change
37
+ _config_change
38
+ end
39
+
40
+ # An override to ActiveRecord's accessor for the sole purpoes of injecting +Serialization+
41
+ # behavior so that we can set the context of this owner and ensure we pass that down to
42
+ # the YAML coder. Doing this on a per instance basis keeps us from trumping all other
43
+ # +ActiveRecord::AttributeMethods::Serialization::Attribute+ objects.
44
+ def _config
45
+ attrib = @attributes['_config']
46
+ unless attrib.respond_to?(:__store_configurable_owner__)
47
+ attrib.extend Serialization
48
+ attrib.__store_configurable_owner__ = self
49
+ end
50
+ super
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,18 @@
1
+ module StoreConfigurable
2
+
3
+ # This module's behavior is injected into +ActiveRecord::AttributeMethods::Serialization::Attribute+
4
+ # class which is a mini state machine for serialized objects. It allows us to both set the store's
5
+ # owner as well as overwrite the +unserialize+ method to give the coder both the YAML and owner
6
+ # context. This is done via the +_config+ attribute reader override.
7
+ module Serialization
8
+
9
+ attr_accessor :__store_configurable_owner__
10
+
11
+ def unserialize
12
+ self.state = :unserialized
13
+ self.value = coder.load(value, __store_configurable_owner__)
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,6 @@
1
+ module StoreConfigurable
2
+
3
+ # We track ActiveRecord's major and minor version and follow semantic versioning.
4
+ VERSION = '3.2.0'
5
+
6
+ end
@@ -0,0 +1,7 @@
1
+ require 'active_record'
2
+ require 'store_configurable/version'
3
+ require 'store_configurable/dirty_options'
4
+ require 'store_configurable/object'
5
+ require 'store_configurable/serialization'
6
+ require 'store_configurable/read'
7
+ require 'store_configurable/base'
@@ -0,0 +1,147 @@
1
+ require 'helper'
2
+
3
+ class StoreConfigurable::BaseTest < StoreConfigurable::TestCase
4
+
5
+ it 'is never blank' do
6
+ new_user.config.wont_be_nil
7
+ end
8
+
9
+ it 'can set and get root attributes' do
10
+ new_user.config.foo = 'foo'
11
+ new_user.config.foo.must_equal 'foo'
12
+ end
13
+
14
+ it 'can set and get adhoc nested options' do
15
+ options = {:this => 'that'}
16
+ new_user.config.foo.bar.options = options
17
+ new_user.config.foo.bar.options.must_equal options
18
+ end
19
+
20
+ it 'can serialize to yaml' do
21
+ user_ken.config.foo = 'bar'
22
+ user_ken.config.to_yaml.must_include '--- !omap'
23
+ user_ken.config.to_yaml.must_include ':foo: bar'
24
+ end
25
+
26
+ it 'wont mark owner as dirty after initial read from database with no existing config' do
27
+ user_ken.config
28
+ user_ken.wont_be :config_changed?
29
+ end
30
+
31
+ it 'can use uncool hash syntax if you want with varying techniques of strings, symbols and calls' do
32
+ user_ken.config.color = 'black'
33
+ user_ken.config['remember_me'] = true
34
+ user_ken.config['sortable_tables'].direction = 'asc'
35
+ user_ken.config.sortable_tables['column'] = 'updated_at'
36
+ user_ken.save!
37
+ user_ken.reload
38
+ user_ken.config['color'].must_equal 'black'
39
+ user_ken.config[:color].must_equal 'black'
40
+ user_ken.config.remember_me.must_equal true
41
+ user_ken.config.sortable_tables[:direction].must_equal 'asc'
42
+ user_ken.config[:sortable_tables][:column].must_equal 'updated_at'
43
+ end
44
+
45
+ it 'must be mark owner as dirty after missing getter since that inits a new namespace' do
46
+ user_ken.config.bar
47
+ user_ken.must_be :config_changed?
48
+ end
49
+
50
+ it 'does not support dup, reject, merge' do
51
+ lambda{ user_ken.config.dup }.must_raise(NotImplementedError)
52
+ lambda{ user_ken.config.reject{} }.must_raise(NotImplementedError)
53
+ lambda{ user_ken.config.merge({}) }.must_raise(NotImplementedError)
54
+ end
55
+
56
+ describe 'existing data' do
57
+
58
+ let(:color) { '#c1c1c1' }
59
+ let(:remember) { true }
60
+ let(:deep_value) { StorableObject.new('test') }
61
+ let(:plugin_opts) { Hash[:sort,'asc',:on,true] }
62
+
63
+ before do
64
+ user_ken.config.color = color
65
+ user_ken.config.remember_me = remember
66
+ user_ken.config.plugin.options = plugin_opts
67
+ user_ken.config.you.should.never.need.to.do.this = deep_value
68
+ user_ken.save!
69
+ @user = User.find(user_ken.id)
70
+ end
71
+
72
+ it 'wont be dirty after loading' do
73
+ @user.wont_be :config_changed?
74
+ end
75
+
76
+ it 'can reconsitute saved values' do
77
+ @user.config.color.must_equal color
78
+ @user.config.remember_me.must_equal remember
79
+ @user.config.plugin.options.must_equal plugin_opts
80
+ @user.config.you.should.never.need.to.do.this.must_equal deep_value
81
+ end
82
+
83
+ it 'wont be dirty after reading saved configs' do
84
+ @user.config.color
85
+ @user.config.remember_me
86
+ @user.config.plugin.options
87
+ @user.config.you.should.never.need.to.do.this
88
+ @user.wont_be :config_changed?
89
+ end
90
+
91
+ it 'wont be dirty when setting same config values' do
92
+ @user.config.color = color
93
+ @user.config.remember_me = remember
94
+ @user.config.plugin.options = plugin_opts
95
+ @user.config.you.should.never.need.to.do.this = deep_value
96
+ @user.wont_be :config_changed?
97
+ end
98
+
99
+ it 'must be marked dirty when values change' do
100
+ @user.config.color = 'black'
101
+ @user.must_be :config_changed?
102
+ @user.save!
103
+ @user.config.color.must_equal 'black'
104
+ end
105
+
106
+ it 'must be marked dirty when clearing' do
107
+ @user.config.clear
108
+ @user.must_be :config_changed?
109
+ @user.save!
110
+ @user.config.must_be :blank?
111
+ end
112
+
113
+ it 'must be marked dirty when deleting a key' do
114
+ @user.config.delete :color
115
+ @user.must_be :config_changed?
116
+ @user.save!
117
+ @user.config.has_key?(:color).must_equal false
118
+ end
119
+
120
+ it 'wont be marked dirty when deleting a non-existent key' do
121
+ @user.config.delete :doesnotexist
122
+ @user.wont_be :config_changed?
123
+ end
124
+
125
+ it 'must be marked dirty when using delete_if' do
126
+ @user.config.delete_if { |k,v| true }
127
+ @user.must_be :config_changed?
128
+ @user.config.must_be :blank?
129
+ end
130
+
131
+ it 'wont be marked dirty when using delete_if and nothing happens' do
132
+ @user.config.delete_if { |k,v| false }
133
+ @user.wont_be :config_changed?
134
+ @user.config.you.should.never.need.to.do.this = deep_value
135
+ end
136
+
137
+ it 'must be marked dirty when using reject! on true' do
138
+ @user.config.reject! { |k,v| true }
139
+ @user.must_be :config_changed?
140
+ @user.config.must_be :blank?
141
+ end
142
+
143
+ end
144
+
145
+
146
+ end
147
+
data/test/helper.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require "bundler/setup"
4
+ Bundler.require
5
+ require 'store_configurable'
6
+ require 'active_record/base'
7
+ require 'minitest/autorun'
8
+ require 'logger'
9
+
10
+
11
+ ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__),'debug.log'))
12
+ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
13
+
14
+
15
+ module StoreConfigurable
16
+ class TestCase < MiniTest::Spec
17
+
18
+ before { setup_environment }
19
+
20
+ let(:new_user) { User.new }
21
+ let(:user_ken) { User.find_by_email('ken@metaskills.net') }
22
+
23
+ def setup_environment
24
+ setup_database
25
+ setup_data
26
+ end
27
+
28
+ protected
29
+
30
+ def setup_database
31
+ ActiveRecord::Base.class_eval do
32
+ silence do
33
+ connection.create_table :users, :force => true do |t|
34
+ t.string :name, :email
35
+ t.text :_config
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ def setup_data
42
+ User.create :name => 'Ken Collins', :email => 'ken@metaskills.net'
43
+ end
44
+
45
+ end
46
+ end
47
+
48
+ class StorableObject
49
+ attr_accessor :value
50
+ def initialize(value) ; @value = value ; end
51
+ def ==(other) ; value == other.value ; end
52
+ end
53
+
54
+ class User < ActiveRecord::Base
55
+ store_configurable
56
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: store_configurable
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ken Collins
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: &70239521805020 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70239521805020
25
+ - !ruby/object:Gem::Dependency
26
+ name: sqlite3
27
+ requirement: &70239521804520 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '1.3'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70239521804520
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70239521804060 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 0.9.2
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70239521804060
47
+ - !ruby/object:Gem::Dependency
48
+ name: minitest
49
+ requirement: &70239521803600 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.8.1
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70239521803600
58
+ description: Grown up ActiveRecord::Store config options!
59
+ email:
60
+ - ken@metaskills.net
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .travis.yml
67
+ - CHANGELOG
68
+ - Gemfile
69
+ - MIT-LICENSE
70
+ - README.md
71
+ - Rakefile
72
+ - lib/store_configurable.rb
73
+ - lib/store_configurable/base.rb
74
+ - lib/store_configurable/dirty_options.rb
75
+ - lib/store_configurable/object.rb
76
+ - lib/store_configurable/read.rb
77
+ - lib/store_configurable/serialization.rb
78
+ - lib/store_configurable/version.rb
79
+ - test/cases/base_test.rb
80
+ - test/helper.rb
81
+ homepage: http://github.com/Decisiv/store_configurable/
82
+ licenses: []
83
+ post_install_message:
84
+ rdoc_options:
85
+ - --charset=UTF-8
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 1.8.17
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: A zero-configuration recursive Hash for storing a tree of options in a serialized
106
+ ActiveRecord column.
107
+ test_files:
108
+ - test/cases/base_test.rb
109
+ - test/helper.rb