activerecord-typedstore 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4903248fd9dd18108eb597807a28c894dddce203
4
+ data.tar.gz: 4ed8e3a9115d1cc1378eb013bc134c682cad5387
5
+ SHA512:
6
+ metadata.gz: ee405d97dacbd41d908f651bbdd5b6cebc7951d05923e09929ed30f085b17a2afa15d81bdae0a9790cb8fb817a02e273c7df27f216f724a206667baf2b55f7da
7
+ data.tar.gz: 566173ae233b5046cd24674d365bfdb4dd8db51753fefa9556f4c3ed8e234c56083d31c96f53e1a5ca71fc37c8eada3ac4eb9b31c7572239b285ed2d114a30ec
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ gemfiles/*.lock
19
+
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ gemfile:
5
+ - gemfiles/Gemfile.ar-3.2
6
+ - gemfiles/Gemfile.ar-4.0
7
+ - gemfiles/Gemfile.ar-edge
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in active_record_typed_store.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jean Boussier
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # ActiveRecord::TypedStore
2
+
3
+ [![Build Status](https://secure.travis-ci.org/byroot/activerecord-typedstore.png)](http://travis-ci.org/byroot/activerecord-typedstore)
4
+ [![Code Climate](https://codeclimate.com/github/byroot/activerecord-typedstore.png)](https://codeclimate.com/github/byroot/activerecord-typedstore)
5
+
6
+ [ActiveRecord::Store](http://api.rubyonrails.org/classes/ActiveRecord/Store.html) but with typed attributes.
7
+
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'activerecord-typedstore'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install activerecord-typedstore
22
+
23
+ ## Usage
24
+
25
+ It works exactly like [ActiveRecord::Store documentation](http://api.rubyonrails.org/classes/ActiveRecord/Store.html) but you need to declare the type of your attributes.
26
+
27
+ Attributes definition is similar to activerecord's migrations:
28
+
29
+ ```ruby
30
+
31
+ class Shop < ActiveRecord::Base
32
+
33
+ typed_store :settings do |s|
34
+ s.boolean :public, default: false
35
+ s.string :email, null: true
36
+ s.datetime :publish_at, null: true
37
+ end
38
+
39
+ end
40
+
41
+ shop = Shop.new(email: 'george@cyclim.se')
42
+ shop.public? # => false
43
+ shop.email # => 'george@cyclim.se'
44
+ shop.published_at # => nil
45
+ ```
46
+
47
+ Type casting rules and attribute behavior are exactly the same as a for real database columns.
48
+ Actually the only difference is that you wont be able to query on these attributes (unless you use Postgres JSON or HStore types) and that you don't need to do a migration to add / remove an attribute.
49
+
50
+ If not please fill an issue.
51
+
52
+ ## Contributing
53
+
54
+ 1. Fork it
55
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
56
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
57
+ 4. Push to the branch (`git push origin my-new-feature`)
58
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_record/typed_store/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'activerecord-typedstore'
8
+ spec.version = ActiveRecord::TypedStore::VERSION
9
+ spec.authors = ['Jean Boussier']
10
+ spec.email = ['jean.boussier@gmail.com']
11
+ spec.description = %q{ActiveRecord::Store but with type definition}
12
+ spec.summary = %q{ActiveRecord::Store but with type definition}
13
+ spec.homepage = 'https://github.com/byroot/activerecord-typedstore'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'activerecord', '>= 3.2'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.3'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'rspec'
26
+ spec.add_development_dependency 'sqlite3'
27
+ spec.add_development_dependency 'database_cleaner'
28
+ end
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 3.2.15'
4
+ gem 'bundler', '~> 1.3'
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'sqlite3'
8
+ gem 'database_cleaner'
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 4.0.0'
4
+ gem 'bundler', '~> 1.3'
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'sqlite3'
8
+ gem 'database_cleaner'
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rails', github: 'rails/rails'
4
+ gem 'bundler', '~> 1.3'
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'sqlite3'
8
+ gem 'database_cleaner'
@@ -0,0 +1,26 @@
1
+ module ActiveRecord::TypedStore
2
+
3
+ class Column < ::ActiveRecord::ConnectionAdapters::Column
4
+
5
+ def initialize(name, type, options={})
6
+ @name = name
7
+ @type = type
8
+ @default = extract_default(options.fetch(:default, nil))
9
+ @null = options.fetch(:null, false)
10
+ end
11
+
12
+ def type_cast(value)
13
+ if type == :string || type == :text
14
+ return value.to_s unless value.nil? && null
15
+ end
16
+
17
+ if IS_AR_3_2 && type == :datetime && value.is_a?(DateTime)
18
+ return super(value.iso8601)
19
+ end
20
+
21
+ super
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,24 @@
1
+ module ActiveRecord::TypedStore
2
+
3
+ class DSL
4
+
5
+ attr_reader :columns
6
+
7
+ def initialize
8
+ @columns = []
9
+ yield self
10
+ end
11
+
12
+ def column_names
13
+ @columns.map(&:name)
14
+ end
15
+
16
+ [:string, :integer, :float, :decimal, :datetime, :date, :boolean].each do |type|
17
+ define_method(type) do |name, options={}|
18
+ @columns << Column.new(name, type, options)
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,79 @@
1
+ require 'active_record/typed_store/column'
2
+ require 'active_record/typed_store/dsl'
3
+
4
+ module ActiveRecord::TypedStore
5
+ AR_VERSION = Gem::Version.new(ActiveRecord::VERSION::STRING)
6
+ IS_AR_3_2 = AR_VERSION < Gem::Version.new('4.0')
7
+ IS_AR_4_0 = AR_VERSION >= Gem::Version.new('4.0') && AR_VERSION < Gem::Version.new('4.1.0.beta')
8
+ IS_AR_4_1 = AR_VERSION >= Gem::Version.new('4.1.0.beta')
9
+
10
+ module Extension
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ class_attribute :stored_typed_attributes, instance_accessor: false
15
+ self.stored_typed_attributes = {}
16
+ end
17
+
18
+ module ClassMethods
19
+
20
+ def typed_store(store_attribute, options={}, &block)
21
+ dsl = DSL.new(&block)
22
+
23
+ store(store_attribute, options.merge(accessors: dsl.column_names))
24
+
25
+ stored_typed_attributes[store_attribute] ||= {}
26
+ stored_typed_attributes[store_attribute].merge!(dsl.columns.index_by(&:name))
27
+
28
+ if IS_AR_4_1 || IS_AR_3_2
29
+ after_initialize { initialize_store_attribute(store_attribute) }
30
+ end
31
+
32
+ if IS_AR_3_2
33
+ dsl.columns.each do |column|
34
+ define_method("#{column.name}_with_type_casting=") do |value|
35
+ self.send("#{column.name}_without_type_casting=", column.type_cast(value))
36
+ end
37
+ alias_method_chain "#{column.name}=", :type_casting
38
+
39
+ define_method(column.name) do
40
+ send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
41
+ store = send(store_attribute)
42
+ store.has_key?(column.name) ? store[column.name] : column.default
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ protected
51
+
52
+ def write_store_attribute(store_attribute, key, value)
53
+ casted_value = value
54
+ if store_definition = self.class.stored_typed_attributes[store_attribute]
55
+ if column_definition = store_definition[key]
56
+ casted_value = column_definition.type_cast(value)
57
+ end
58
+ end
59
+ super(store_attribute, key, casted_value)
60
+ end
61
+
62
+ private
63
+
64
+ def initialize_store_attribute(store_attribute)
65
+ attribute = IS_AR_4_0 ? super : send(store_attribute)
66
+ if columns = self.class.stored_typed_attributes[store_attribute]
67
+ columns.each do |name, definition|
68
+ if attribute.has_key?(name)
69
+ attribute[name] = definition.type_cast(attribute[name])
70
+ else
71
+ attribute[name] = definition.default if definition.has_default?
72
+ end
73
+ end
74
+ end
75
+ attribute
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module TypedStore
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_support'
2
+
3
+ module ActiveRecord
4
+ module TypedStore
5
+ end
6
+ end
7
+
8
+ ActiveSupport.on_load(:active_record) do
9
+ require 'active_record/typed_store/extension'
10
+ ::ActiveRecord::Base.send :include, ActiveRecord::TypedStore::Extension
11
+ end
@@ -0,0 +1,294 @@
1
+ require 'spec_helper'
2
+
3
+ ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING)
4
+ ar_4_0 = Gem::Version.new('4.0')
5
+ ar_4_1 = Gem::Version.new('4.1.0.beta')
6
+
7
+ shared_examples 'a model' do
8
+
9
+ let(:model) { described_class.new }
10
+
11
+ describe 'build' do
12
+
13
+ it 'assign attributes received by #initialize' do
14
+ model = described_class.new(public: true)
15
+ expect(model.public).to be_true
16
+ end
17
+
18
+ end
19
+
20
+ describe 'unknown attribute' do
21
+
22
+ it 'raise an ActiveRecord::UnknownAttributeError on save attemps' do
23
+ expect {
24
+ model.update_attributes(unknown_attribute: 42)
25
+ }.to raise_error ActiveRecord::UnknownAttributeError
26
+ end
27
+
28
+ it 'raise a NoMethodError on assignation attemps' do
29
+ expect {
30
+ model.unknown_attribute = 42
31
+ }.to raise_error NoMethodError
32
+ end
33
+
34
+ end
35
+
36
+ describe 'all attributes' do
37
+
38
+ it 'is initialized at nil if :default is not defined' do
39
+ expect(model.no_default).to be_nil
40
+ end
41
+
42
+ end
43
+
44
+ describe 'string attribute' do
45
+
46
+ it 'has the defined default as initial value' do
47
+ expect(model.name).to be == ''
48
+ end
49
+
50
+ it 'properly cast the value as string' do
51
+ model.update_attributes(name: 42)
52
+ expect(model.reload.name).to be == '42'
53
+ end
54
+
55
+ end
56
+
57
+ describe 'boolean attribute' do
58
+
59
+ it 'has the defined :default as initial value' do
60
+ expect(model.public).to be_false
61
+ end
62
+
63
+ [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].each do |value|
64
+
65
+ it "cast `#{value.inspect}` as `true`" do
66
+ model.public = value
67
+ expect(model.public).to be_true
68
+ end
69
+
70
+ end
71
+
72
+ [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].each do |value|
73
+
74
+ it "cast `#{value.inspect}` as `false`" do
75
+ model.public = value
76
+ expect(model.public).to be_false
77
+ end
78
+
79
+ end
80
+
81
+ it 'properly persit the value' do
82
+ model.update_attributes(public: false)
83
+ expect(model.reload.public).to be_false
84
+ model.update_attributes(public: true)
85
+ expect(model.reload.public).to be_true
86
+ end
87
+
88
+ it 'initialize with default value if the column is not nullable' do
89
+ expect(model.public).to be_false
90
+ model.save
91
+ expect(model.reload.public).to be_false
92
+ end
93
+
94
+ it 'can store nil if the column is nullable' do
95
+ model.update_attributes(enabled: nil)
96
+ expect(model.reload.enabled).to be_nil
97
+ end
98
+
99
+ it 'save the default value if the column is nullable but the value not explictly set' do
100
+ model.save
101
+ expect(model.reload.enabled).to be_true
102
+ end
103
+
104
+ end
105
+
106
+ describe 'integer attributes' do
107
+
108
+ it 'has the defined default as initial value' do
109
+ expect(model.age).to be_zero
110
+ end
111
+
112
+ it 'properly cast assigned value to integer' do
113
+ model.age = '42'
114
+ expect(model.age).to be == 42
115
+ end
116
+
117
+ it 'properly cast non numeric values to integer' do
118
+ model.age = 'foo'
119
+ expect(model.age).to be == 0
120
+ end
121
+
122
+ it 'can store nil if the column is nullable' do
123
+ model.update_attributes(max_length: nil)
124
+ expect(model.reload.max_length).to be_nil
125
+ end
126
+
127
+ end
128
+
129
+ describe 'float attributes' do
130
+
131
+ it 'has the defined default as initial value' do
132
+ expect(model.rate).to be_zero
133
+ end
134
+
135
+ it 'properly cast assigned value to float' do
136
+ model.rate = '4.2'
137
+ expect(model.rate).to be == 4.2
138
+ end
139
+
140
+ it 'properly cast non numeric values to float' do
141
+ model.rate = 'foo'
142
+ expect(model.rate).to be == 0
143
+ end
144
+
145
+ it 'can store nil if the column is nullable' do
146
+ model.update_attributes(price: nil)
147
+ expect(model.reload.price).to be_nil
148
+ end
149
+
150
+ end
151
+
152
+ describe 'decimal attributes' do
153
+
154
+ it 'has the defined default as initial value' do
155
+ expect(model.total_price).to be == BigDecimal.new('4.2')
156
+ expect(model.total_price).to be_a BigDecimal
157
+ end
158
+
159
+ it 'properly cast assigned value to decimal' do
160
+ model.shipping_cost = 4.2
161
+ expect(model.shipping_cost).to be == BigDecimal.new('4.2')
162
+ expect(model.shipping_cost).to be_a BigDecimal
163
+ end
164
+
165
+ it 'properly cast non numeric values to decimal' do
166
+ model.total_price = 'foo'
167
+ expect(model.total_price).to be == 0
168
+ expect(model.total_price).to be_a BigDecimal
169
+ end
170
+
171
+ it 'retreive a BigDecimal instance' do
172
+ model.update_attributes(shipping_cost: 4.2)
173
+ expect(model.reload.shipping_cost).to be == BigDecimal.new('4.2')
174
+ expect(model.reload.shipping_cost).to be_a BigDecimal
175
+ end
176
+
177
+ it 'can store nil if the column is nullable' do
178
+ model.update_attributes(shipping_cost: nil)
179
+ expect(model.reload.shipping_cost).to be_nil
180
+ end
181
+
182
+ end
183
+
184
+ describe 'date attributes' do
185
+
186
+ let(:date) { Date.new(1984, 6, 8) }
187
+
188
+ it 'has the defined default as initial value' do
189
+ expect(model.published_on).to be == date
190
+ end
191
+
192
+ it 'properly cast assigned value to date' do
193
+ model.remind_on = '1984-06-08'
194
+ expect(model.remind_on).to be == date
195
+ end
196
+
197
+ it 'retreive a Date instance' do
198
+ model.update_attributes(published_on: date)
199
+ expect(model.reload.published_on).to be == date
200
+ end
201
+
202
+ it 'nillify unparsable dates' do
203
+ model.published_on = 'foo'
204
+ expect(model.published_on).to be_nil
205
+ end
206
+
207
+ it 'can store nil if the column is nullable' do
208
+ model.update_attributes(remind_on: nil)
209
+ expect(model.reload.remind_on).to be_nil
210
+ end
211
+
212
+ end
213
+
214
+ describe 'datetime attributes' do
215
+
216
+ let(:datetime) { DateTime.new(1984, 6, 8, 13, 57, 12) }
217
+ let(:datetime_string) { '1984-06-08 13:57:12' }
218
+ let(:time) { Time.parse(datetime_string) }
219
+
220
+ context "with ActiveRecord #{ActiveRecord::VERSION::STRING}" do
221
+
222
+ if ar_version < ar_4_0
223
+
224
+ it 'has the defined default as initial value' do
225
+ model.save
226
+ expect(model.published_at).to be == time
227
+ end
228
+
229
+ it 'properly cast assigned value to time' do
230
+ model.remind_at = datetime_string
231
+ expect(model.remind_at).to be == time
232
+ end
233
+
234
+ it 'properly cast assigned value to time on save' do
235
+ model.remind_at = datetime_string
236
+ model.save
237
+ model.reload
238
+ expect(model.remind_at).to be == time
239
+ end
240
+
241
+ it 'retreive a Time instance' do
242
+ model.update_attributes(published_at: datetime)
243
+ expect(model.reload.published_at).to be == time
244
+ end
245
+
246
+ else
247
+
248
+ it 'has the defined default as initial value' do
249
+ model.save
250
+ expect(model.reload.published_at).to be == datetime
251
+ end
252
+
253
+ it 'retreive a DateTime instance' do
254
+ model.update_attributes(published_at: datetime)
255
+ expect(model.reload.published_at).to be == datetime
256
+ end
257
+
258
+ it 'properly cast assigned value to datetime' do
259
+ model.remind_at = datetime_string
260
+ expect(model.remind_at).to be == datetime
261
+ end
262
+ end
263
+
264
+ end
265
+
266
+ it 'nillify unparsable datetimes' do
267
+ model.published_at = 'foo'
268
+ expect(model.published_at).to be_nil
269
+ end
270
+
271
+ it 'can store nil if the column is nullable' do
272
+ model.update_attributes(remind_at: nil)
273
+ expect(model.reload.remind_at).to be_nil
274
+ end
275
+
276
+ end
277
+
278
+ end
279
+
280
+ describe RegularARModel do
281
+ it_should_behave_like 'a model'
282
+ end
283
+
284
+ describe YamlTypedStoreModel do
285
+ it_should_behave_like 'a model'
286
+ end
287
+
288
+ describe JsonTypedStoreModel do
289
+ it_should_behave_like 'a model'
290
+ end
291
+
292
+ describe MarshalTypedStoreModel do
293
+ it_should_behave_like 'a model'
294
+ end
@@ -0,0 +1,14 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'database_cleaner'
5
+ require 'active_record/typed_store'
6
+
7
+ Dir[File.expand_path(File.join(File.dirname(__FILE__), 'support', '**', '*.rb'))].each { |f| require f }
8
+
9
+ RSpec.configure do |config|
10
+ config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ config.run_all_when_everything_filtered = true
12
+ config.filter_run :focus
13
+ config.order = 'random'
14
+ end
@@ -0,0 +1,13 @@
1
+ DatabaseCleaner[:active_record].strategy = :transaction
2
+
3
+ RSpec.configure do |config|
4
+ config.before :suite do
5
+ DatabaseCleaner.clean_with :transaction
6
+ end
7
+ config.before :each do
8
+ DatabaseCleaner.start
9
+ end
10
+ config.after :each do
11
+ DatabaseCleaner.clean
12
+ end
13
+ end
@@ -0,0 +1,81 @@
1
+ require 'active_record'
2
+ require 'json'
3
+ require 'yaml'
4
+
5
+ ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
6
+ ActiveRecord::Base.establish_connection('test')
7
+
8
+ def define_columns(t)
9
+ t.integer :no_default
10
+
11
+ t.string :name, default: ''
12
+ t.string :email, null: true
13
+
14
+ t.boolean :public, default: false
15
+ t.boolean :enabled, default: true, null: true
16
+
17
+ t.integer :age, default: 0
18
+ t.integer :max_length, null: true
19
+
20
+ t.float :rate, default: 0
21
+ t.float :price, null: true
22
+
23
+ t.date :published_on, default: '1984-06-08'
24
+ t.date :remind_on, null: true
25
+
26
+ t.datetime :published_at, default: '1984-06-08 13:57:12'
27
+ t.datetime :remind_at, null: true
28
+
29
+ t.decimal :total_price, default: 4.2
30
+ t.decimal :shipping_cost, null: true
31
+
32
+ end
33
+
34
+ class CreateAllTables < ActiveRecord::Migration
35
+ def self.up
36
+ create_table(:regular_ar_models) { |t| define_columns(t) }
37
+ create_table(:yaml_typed_store_models) { |t| t.text :settings }
38
+ create_table(:json_typed_store_models) { |t| t.text :settings }
39
+ create_table(:marshal_typed_store_models) { |t| t.text :settings }
40
+ end
41
+ end
42
+ ActiveRecord::Migration.verbose = false
43
+ CreateAllTables.up
44
+
45
+ class RegularARModel < ActiveRecord::Base
46
+ end
47
+
48
+ class YamlTypedStoreModel < ActiveRecord::Base
49
+ typed_store :settings do |s|
50
+ define_columns(s)
51
+ end
52
+ end
53
+
54
+ class ColumnCoder
55
+
56
+ def initialize(coder)
57
+ @coder = coder
58
+ end
59
+
60
+ def load(data)
61
+ return {} unless data
62
+ @coder.load(data)
63
+ end
64
+
65
+ def dump(data)
66
+ @coder.dump(data || {})
67
+ end
68
+
69
+ end
70
+
71
+ class JsonTypedStoreModel < ActiveRecord::Base
72
+ typed_store :settings, coder: ColumnCoder.new(JSON) do |s|
73
+ define_columns(s)
74
+ end
75
+ end
76
+
77
+ class MarshalTypedStoreModel < ActiveRecord::Base
78
+ typed_store :settings, coder: ColumnCoder.new(Marshal) do |s|
79
+ define_columns(s)
80
+ end
81
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-typedstore
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jean Boussier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: database_cleaner
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: ActiveRecord::Store but with type definition
98
+ email:
99
+ - jean.boussier@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .rspec
106
+ - .travis.yml
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - activerecord-typedstore.gemspec
112
+ - gemfiles/Gemfile.ar-3.2
113
+ - gemfiles/Gemfile.ar-4.0
114
+ - gemfiles/Gemfile.ar-edge
115
+ - lib/active_record/typed_store.rb
116
+ - lib/active_record/typed_store/column.rb
117
+ - lib/active_record/typed_store/dsl.rb
118
+ - lib/active_record/typed_store/extension.rb
119
+ - lib/active_record/typed_store/version.rb
120
+ - spec/active_record/typed_store_spec.rb
121
+ - spec/spec_helper.rb
122
+ - spec/support/database_cleaner.rb
123
+ - spec/support/models.rb
124
+ homepage: https://github.com/byroot/activerecord-typedstore
125
+ licenses:
126
+ - MIT
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubyforge_project:
144
+ rubygems_version: 2.1.4
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: ActiveRecord::Store but with type definition
148
+ test_files:
149
+ - spec/active_record/typed_store_spec.rb
150
+ - spec/spec_helper.rb
151
+ - spec/support/database_cleaner.rb
152
+ - spec/support/models.rb