activerecord-typedstore 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4903248fd9dd18108eb597807a28c894dddce203
4
- data.tar.gz: 4ed8e3a9115d1cc1378eb013bc134c682cad5387
3
+ metadata.gz: 6afeb6ff2bb89ff627f8705a5e7006db18f056f2
4
+ data.tar.gz: 502372b9082fb76972f43abc216d4cc8d95c4be2
5
5
  SHA512:
6
- metadata.gz: ee405d97dacbd41d908f651bbdd5b6cebc7951d05923e09929ed30f085b17a2afa15d81bdae0a9790cb8fb817a02e273c7df27f216f724a206667baf2b55f7da
7
- data.tar.gz: 566173ae233b5046cd24674d365bfdb4dd8db51753fefa9556f4c3ed8e234c56083d31c96f53e1a5ca71fc37c8eada3ac4eb9b31c7572239b285ed2d114a30ec
6
+ metadata.gz: 0218e36856f3c37c08c0997f349ab7c610b528a1de19dd9bf24d312559922648a5ac9271fba86696254911688a51b62c0ee11500ed6b4af4d392834cda115126
7
+ data.tar.gz: cc586d21280e82597c3cdd11390737ca7286997fc87f6cdac785749fcda3754b10fdd9174eea633da2078dca0605c6b00fa2ec35e44f7e72da314eab6247632f
data/.travis.yml CHANGED
@@ -5,3 +5,7 @@ gemfile:
5
5
  - gemfiles/Gemfile.ar-3.2
6
6
  - gemfiles/Gemfile.ar-4.0
7
7
  - gemfiles/Gemfile.ar-edge
8
+
9
+ before_script:
10
+ - mysql -e 'create database typed_store_test;'
11
+ - psql -c 'create database typed_store_test;' -U postgres
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Build Status](https://secure.travis-ci.org/byroot/activerecord-typedstore.png)](http://travis-ci.org/byroot/activerecord-typedstore)
4
4
  [![Code Climate](https://codeclimate.com/github/byroot/activerecord-typedstore.png)](https://codeclimate.com/github/byroot/activerecord-typedstore)
5
+ [![Coverage Status](https://coveralls.io/repos/byroot/activerecord-typedstore/badge.png)](https://coveralls.io/r/byroot/activerecord-typedstore)
5
6
 
6
7
  [ActiveRecord::Store](http://api.rubyonrails.org/classes/ActiveRecord/Store.html) but with typed attributes.
7
8
 
@@ -22,7 +23,7 @@ Or install it yourself as:
22
23
 
23
24
  ## Usage
24
25
 
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
+ It works exactly like [ActiveRecord::Store documentation](http://api.rubyonrails.org/classes/ActiveRecord/Store.html) but you can declare the type of your attributes.
26
27
 
27
28
  Attributes definition is similar to activerecord's migrations:
28
29
 
@@ -31,23 +32,52 @@ Attributes definition is similar to activerecord's migrations:
31
32
  class Shop < ActiveRecord::Base
32
33
 
33
34
  typed_store :settings do |s|
34
- s.boolean :public, default: false
35
- s.string :email, null: true
36
- s.datetime :publish_at, null: true
35
+ s.boolean :public, default: false, null: false
36
+ s.string :email
37
+ s.datetime :publish_at
38
+ s.integer :age, null: false
39
+
40
+ # You can define array attributes like in rails 4 and postgres
41
+ s.string :tags, array: true, default: [], null: false
42
+
43
+ # In addition to prevent null values you can prevent blank values
44
+ s.string :title, blank: false, default: 'Title'
45
+
46
+ # If you don't want to enforce a datatype but still like to have default handling
47
+ s.any :source, blank: false, default: 'web'
37
48
  end
38
49
 
50
+ # You can use any ActiveModel validator
51
+ validates :age, presence: true
52
+
39
53
  end
40
54
 
55
+ # Values are accessible like normal model attributes
41
56
  shop = Shop.new(email: 'george@cyclim.se')
42
57
  shop.public? # => false
43
58
  shop.email # => 'george@cyclim.se'
44
59
  shop.published_at # => nil
60
+
61
+ # Values are type casted
62
+ shop.update_attributes(
63
+ age: '42',
64
+ published_at: '1984-06-08 13:57:12'
65
+ )
66
+ shop.age # => 42
67
+ shop.published_at.class #= DateTime
68
+
69
+ # You can still use it as a regular store
70
+ shop.settings[:unknown] = 'Hello World'
71
+ shop.save
72
+ shop.reload
73
+ shop.settings[:unknown] # => 'Hello World'
74
+
45
75
  ```
46
76
 
47
77
  Type casting rules and attribute behavior are exactly the same as a for real database columns.
48
78
  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
79
 
50
- If not please fill an issue.
80
+ If not, please fill an issue.
51
81
 
52
82
  ## Contributing
53
83
 
@@ -23,6 +23,9 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency 'bundler', '~> 1.3'
24
24
  spec.add_development_dependency 'rake'
25
25
  spec.add_development_dependency 'rspec'
26
+ spec.add_development_dependency 'coveralls'
26
27
  spec.add_development_dependency 'sqlite3'
28
+ spec.add_development_dependency 'pg'
29
+ spec.add_development_dependency 'mysql2'
27
30
  spec.add_development_dependency 'database_cleaner'
28
31
  end
@@ -5,4 +5,7 @@ gem 'bundler', '~> 1.3'
5
5
  gem 'rake'
6
6
  gem 'rspec'
7
7
  gem 'sqlite3'
8
+ gem 'pg', '~> 0.11'
9
+ gem 'mysql2'
8
10
  gem 'database_cleaner'
11
+ gem 'coveralls', require: false
@@ -5,4 +5,8 @@ gem 'bundler', '~> 1.3'
5
5
  gem 'rake'
6
6
  gem 'rspec'
7
7
  gem 'sqlite3'
8
+ gem 'pg', '~> 0.11'
9
+ gem 'mysql2'
8
10
  gem 'database_cleaner'
11
+ gem 'coveralls', require: false
12
+ gem 'debugger'
@@ -5,4 +5,7 @@ gem 'bundler', '~> 1.3'
5
5
  gem 'rake'
6
6
  gem 'rspec'
7
7
  gem 'sqlite3'
8
+ gem 'pg', '~> 0.11'
9
+ gem 'mysql2'
8
10
  gem 'database_cleaner'
11
+ gem 'coveralls', require: false
@@ -0,0 +1,74 @@
1
+ module ActiveRecord::TypedStore
2
+
3
+ module AR32Fallbacks
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+
8
+ def typed_store(store_attribute, options={}, &block)
9
+ dsl = super
10
+ _ar_32_fallback_accessors(store_attribute, dsl.columns)
11
+ end
12
+
13
+ protected
14
+
15
+ def _ar_32_fallback_accessors(store_attribute, columns)
16
+ _ar_32_fallback_initializer(store_attribute, columns)
17
+ columns.each do |column|
18
+ _ar_32_fallback_accessor(store_attribute, column)
19
+ end
20
+ end
21
+
22
+ def _ar_32_fallback_initializer(store_attribute, columns)
23
+ define_method(store_attribute) do
24
+ initialized = "@_#{store_attribute}_initialized"
25
+ unless instance_variable_get(initialized)
26
+ store = read_attribute(store_attribute)
27
+ store = initialize_store(store, columns)
28
+ write_attribute(store_attribute, store)
29
+ instance_variable_set(initialized, true)
30
+ end
31
+ read_attribute(store_attribute)
32
+ end
33
+ end
34
+
35
+ def _ar_32_fallback_accessor(store_attribute, column)
36
+ _ar_32_fallback_writer(store_attribute, column)
37
+ _ar_32_fallback_reader(store_attribute, column)
38
+ end
39
+
40
+ def _ar_32_fallback_writer(store_attribute, column)
41
+ define_method("#{column.name}_with_type_casting=") do |value|
42
+ self.send("#{column.name}_without_type_casting=", column.cast(value))
43
+ end
44
+ alias_method_chain "#{column.name}=", :type_casting
45
+ end
46
+
47
+ def _ar_32_fallback_reader(store_attribute, column)
48
+ define_method(column.name) do
49
+ send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
50
+ store = send(store_attribute)
51
+
52
+ store.has_key?(column.name) ? store[column.name] : column.default
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ def reload(*)
59
+ _ar_32_reload_stores!
60
+ super
61
+ end
62
+
63
+ private
64
+
65
+ def _ar_32_reload_stores!
66
+ self.class.stored_typed_attributes.keys.each do |store_attribute|
67
+ instance_variable_set("@_#{store_attribute}_initialized", false)
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ ActiveRecord::Base.send(:include, AR32Fallbacks)
74
+ end
@@ -0,0 +1,45 @@
1
+ module ActiveRecord::TypedStore
2
+
3
+ module AR41Fallbacks
4
+
5
+ def reload(*)
6
+ _ar_41_reload_stores!
7
+ super
8
+ end
9
+
10
+ private
11
+
12
+ def _ar_41_reload_stores!
13
+ self.class.stored_typed_attributes.keys.each do |store_attribute|
14
+ instance_variable_set("@_#{store_attribute}_initialized", false)
15
+ end
16
+ end
17
+
18
+ module HashAccessorPatch
19
+
20
+ def self.extended(hash_accessor)
21
+ hash_accessor.singleton_class.alias_method_chain :prepare, :initialization
22
+ end
23
+
24
+ protected
25
+
26
+ def prepare_with_initialization(object, attribute)
27
+ prepare_without_initialization(object, attribute)
28
+
29
+ initialized = "@_#{attribute}_initialized"
30
+ unless object.instance_variable_get(initialized)
31
+ store = object.send(:initialize_store_attribute, attribute)
32
+ object.send("#{attribute}=", store)
33
+ object.instance_variable_set(initialized, true)
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+
40
+ ActiveRecord::Store::HashAccessor.extend(HashAccessorPatch)
41
+ ActiveRecord::Store::IndifferentHashAccessor.extend(HashAccessorPatch)
42
+ end
43
+
44
+ ActiveRecord::Base.send(:include, AR41Fallbacks)
45
+ end
@@ -1,24 +1,42 @@
1
1
  module ActiveRecord::TypedStore
2
2
 
3
3
  class Column < ::ActiveRecord::ConnectionAdapters::Column
4
+ attr_reader :array, :blank
4
5
 
5
6
  def initialize(name, type, options={})
6
7
  @name = name
7
8
  @type = type
9
+ @array = options.fetch(:array, false)
8
10
  @default = extract_default(options.fetch(:default, nil))
9
- @null = options.fetch(:null, false)
11
+ @null = options.fetch(:null, true)
12
+ @blank = options.fetch(:blank, true)
10
13
  end
11
14
 
12
- def type_cast(value)
15
+ def cast(value)
16
+ casted_value = type_cast(value)
17
+ if !blank
18
+ casted_value = default if casted_value.blank?
19
+ elsif !null
20
+ casted_value = default if casted_value.nil?
21
+ end
22
+ casted_value
23
+ end
24
+
25
+ def type_cast(value, map=true)
26
+ if array && (map || value.is_a?(Array))
27
+ return [] if map && !value.is_a?(Array)
28
+ return value.map{ |v| type_cast(v, false) }
29
+ end
30
+
13
31
  if type == :string || type == :text
14
- return value.to_s unless value.nil? && null
32
+ return value.to_s unless value.nil? && (null || array)
15
33
  end
16
34
 
17
35
  if IS_AR_3_2 && type == :datetime && value.is_a?(DateTime)
18
36
  return super(value.iso8601)
19
37
  end
20
38
 
21
- super
39
+ super(value)
22
40
  end
23
41
 
24
42
  end
@@ -13,7 +13,7 @@ module ActiveRecord::TypedStore
13
13
  @columns.map(&:name)
14
14
  end
15
15
 
16
- [:string, :integer, :float, :decimal, :datetime, :date, :boolean].each do |type|
16
+ [:string, :integer, :float, :decimal, :datetime, :date, :boolean, :any].each do |type|
17
17
  define_method(type) do |name, options={}|
18
18
  @columns << Column.new(name, type, options)
19
19
  end
@@ -13,6 +13,8 @@ module ActiveRecord::TypedStore
13
13
  included do
14
14
  class_attribute :stored_typed_attributes, instance_accessor: false
15
15
  self.stored_typed_attributes = {}
16
+ require 'active_record/typed_store/ar_32_fallbacks' if IS_AR_3_2
17
+ require 'active_record/typed_store/ar_41_fallbacks' if IS_AR_4_1
16
18
  end
17
19
 
18
20
  module ClassMethods
@@ -25,24 +27,7 @@ module ActiveRecord::TypedStore
25
27
  stored_typed_attributes[store_attribute] ||= {}
26
28
  stored_typed_attributes[store_attribute].merge!(dsl.columns.index_by(&:name))
27
29
 
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
30
+ dsl
46
31
  end
47
32
 
48
33
  end
@@ -53,26 +38,32 @@ module ActiveRecord::TypedStore
53
38
  casted_value = value
54
39
  if store_definition = self.class.stored_typed_attributes[store_attribute]
55
40
  if column_definition = store_definition[key]
56
- casted_value = column_definition.type_cast(value)
41
+ casted_value = column_definition.cast(value)
57
42
  end
58
43
  end
44
+
59
45
  super(store_attribute, key, casted_value)
60
46
  end
61
47
 
62
48
  private
63
49
 
64
50
  def initialize_store_attribute(store_attribute)
65
- attribute = IS_AR_4_0 ? super : send(store_attribute)
51
+ store = IS_AR_4_0 ? super : send(store_attribute)
66
52
  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
53
+ store = initialize_store(store, columns.values)
54
+ end
55
+ store
56
+ end
57
+
58
+ def initialize_store(store, columns)
59
+ columns.each do |column|
60
+ if store.has_key?(column.name)
61
+ store[column.name] = column.cast(store[column.name])
62
+ else
63
+ store[column.name] = column.default if column.has_default?
73
64
  end
74
65
  end
75
- attribute
66
+ store
76
67
  end
77
68
 
78
69
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module TypedStore
3
- VERSION = '0.0.1'
3
+ VERSION = '0.1.0'
4
4
  end
5
5
  end
@@ -4,10 +4,19 @@ ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING)
4
4
  ar_4_0 = Gem::Version.new('4.0')
5
5
  ar_4_1 = Gem::Version.new('4.1.0.beta')
6
6
 
7
- shared_examples 'a model' do
7
+ shared_examples 'any model' do
8
8
 
9
9
  let(:model) { described_class.new }
10
10
 
11
+ describe 'regular AR::Store' do
12
+
13
+ it 'save attributes as usual' do
14
+ model.update_attributes(title: 'The Big Lebowski')
15
+ expect(model.reload.title).to be == 'The Big Lebowski'
16
+ end
17
+
18
+ end
19
+
11
20
  describe 'build' do
12
21
 
13
22
  it 'assign attributes received by #initialize' do
@@ -106,7 +115,7 @@ shared_examples 'a model' do
106
115
  describe 'integer attributes' do
107
116
 
108
117
  it 'has the defined default as initial value' do
109
- expect(model.age).to be_zero
118
+ expect(model.age).to be == 12
110
119
  end
111
120
 
112
121
  it 'properly cast assigned value to integer' do
@@ -200,8 +209,8 @@ shared_examples 'a model' do
200
209
  end
201
210
 
202
211
  it 'nillify unparsable dates' do
203
- model.published_on = 'foo'
204
- expect(model.published_on).to be_nil
212
+ model.update_attributes(remind_on: 'foo')
213
+ expect(model.remind_on).to be_nil
205
214
  end
206
215
 
207
216
  it 'can store nil if the column is nullable' do
@@ -264,8 +273,8 @@ shared_examples 'a model' do
264
273
  end
265
274
 
266
275
  it 'nillify unparsable datetimes' do
267
- model.published_at = 'foo'
268
- expect(model.published_at).to be_nil
276
+ model.update_attributes(remind_at: 'foo')
277
+ expect(model.remind_at).to be_nil
269
278
  end
270
279
 
271
280
  it 'can store nil if the column is nullable' do
@@ -277,18 +286,153 @@ shared_examples 'a model' do
277
286
 
278
287
  end
279
288
 
280
- describe RegularARModel do
281
- it_should_behave_like 'a model'
289
+ shared_examples 'a store' do
290
+
291
+ let(:model) { described_class.new }
292
+
293
+ describe 'attributes' do
294
+
295
+ it 'retrieve default if assigned nil and null not allowed' do
296
+ model.update_attributes(age: nil)
297
+ expect(model.age).to be == 12
298
+ end
299
+
300
+ it 'retreive default if assigned a blank value and column cannot be blank' do
301
+ model.update_attributes(nickname: '')
302
+ expect(model.reload.nickname).to be == 'Please enter your nickname'
303
+ end
304
+
305
+ end
306
+
307
+ describe '`any` attributes' do
308
+
309
+ it 'accept any type' do
310
+ model.update_attributes(author: 'George')
311
+ expect(model.reload.author).to be == 'George'
312
+
313
+ model.update_attributes(author: 42)
314
+ expect(model.reload.author).to be == 42
315
+ end
316
+
317
+ it 'still handle default' do
318
+ model.update_attributes(source: '')
319
+ expect(model.reload.source).to be == 'web'
320
+ end
321
+
322
+ end
323
+
324
+ end
325
+
326
+ shared_examples 'a db backed model' do
327
+
328
+ let(:model) { described_class.new }
329
+
330
+ it 'let the underlying db raise if assigned nil on non nullable column' do
331
+ expect {
332
+ model.update_attributes(age: nil)
333
+ }.to raise_error(ActiveRecord::StatementInvalid)
334
+ end
335
+
336
+ end
337
+
338
+ shared_examples 'a model supporting arrays' do |regular=false|
339
+
340
+ let(:model) { described_class.new }
341
+
342
+ it 'retrieve an array of values' do
343
+ model.update_attributes(grades: [1, 2, 3, 4])
344
+ expect(model.reload.grades).to be == [1, 2, 3, 4]
345
+ end
346
+
347
+ it 'cast values inside the array (integer)' do
348
+ pending('ActiveRecord bug: https://github.com/rails/rails/pull/11245') if regular
349
+ model.update_attributes(grades: ['1', 2, 3.4])
350
+ expect(model.reload.grades).to be == [1, 2, 3]
351
+ end
352
+
353
+ it 'cast values inside the array (string)' do
354
+ model.update_attributes(tags: [1, 2.3])
355
+ expect(model.reload.tags).to be == %w(1 2.3)
356
+ end
357
+
358
+ it 'accept nil inside array even if collumn is non nullable' do
359
+ model.update_attributes(tags: [1, nil])
360
+ expect(model.reload.tags).to be == ['1', nil]
361
+ end
362
+
363
+ it 'convert non array value as empty array' do
364
+ model.update_attributes(grades: 'foo')
365
+ expect(model.reload.grades).to be == []
366
+ end
367
+
368
+ if !regular || ar_version == ar_4_1
369
+ it 'accept multidimensianl arrays' do
370
+ model.update_attributes(grades: [[1, 2], [3, 4]])
371
+ expect(model.reload.grades).to be == [[1, 2], [3, 4]]
372
+ end
373
+ end
374
+
375
+ if regular
376
+
377
+ it 'raise on non rectangular multidimensianl arrays' do
378
+ expect{
379
+ model.update_attributes(grades: [[1, 2], [3, 4, 5]])
380
+ }.to raise_error(ActiveRecord::StatementInvalid)
381
+ end
382
+
383
+ it 'raise on non nil assignation if column is non nullable' do
384
+ expect{
385
+ model.update_attributes(tags: nil)
386
+ }.to raise_error(ActiveRecord::StatementInvalid)
387
+ end
388
+
389
+ else
390
+
391
+ it 'accept non rectangular multidimensianl arrays' do
392
+ model.update_attributes(grades: [[1, 2], [3, 4, 5]])
393
+ expect(model.reload.grades).to be == [[1, 2], [3, 4, 5]]
394
+ end
395
+
396
+ it 'retreive default if assigned null' do
397
+ model.update_attributes(tags: nil)
398
+ expect(model.reload.tags).to be == []
399
+ end
400
+
401
+ end
402
+
403
+ end
404
+
405
+ describe Sqlite3RegularARModel do
406
+ ActiveRecord::Base.establish_connection('test_sqlite3')
407
+ it_should_behave_like 'any model'
408
+ it_should_behave_like 'a db backed model'
409
+ end
410
+
411
+ describe MysqlRegularARModel do
412
+ it_should_behave_like 'any model'
413
+ it_should_behave_like 'a db backed model'
414
+ end
415
+
416
+ describe PostgresqlRegularARModel do
417
+ it_should_behave_like 'any model'
418
+ it_should_behave_like 'a db backed model'
419
+ it_should_behave_like 'a model supporting arrays', true if ar_version >= ar_4_0
282
420
  end
283
421
 
284
422
  describe YamlTypedStoreModel do
285
- it_should_behave_like 'a model'
423
+ it_should_behave_like 'any model'
424
+ it_should_behave_like 'a store'
425
+ it_should_behave_like 'a model supporting arrays'
286
426
  end
287
427
 
288
428
  describe JsonTypedStoreModel do
289
- it_should_behave_like 'a model'
429
+ it_should_behave_like 'any model'
430
+ it_should_behave_like 'a store'
431
+ it_should_behave_like 'a model supporting arrays'
290
432
  end
291
433
 
292
434
  describe MarshalTypedStoreModel do
293
- it_should_behave_like 'a model'
435
+ it_should_behave_like 'any model'
436
+ it_should_behave_like 'a store'
437
+ it_should_behave_like 'a model supporting arrays'
294
438
  end
data/spec/spec_helper.rb CHANGED
@@ -2,6 +2,15 @@ lib = File.expand_path('../lib', __FILE__)
2
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  require 'database_cleaner'
5
+
6
+ require 'simplecov'
7
+ require 'coveralls'
8
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
9
+ SimpleCov::Formatter::HTMLFormatter,
10
+ Coveralls::SimpleCov::Formatter
11
+ ]
12
+ SimpleCov.start
13
+
5
14
  require 'active_record/typed_store'
6
15
 
7
16
  Dir[File.expand_path(File.join(File.dirname(__FILE__), 'support', '**', '*.rb'))].each { |f| require f }
@@ -2,52 +2,92 @@ require 'active_record'
2
2
  require 'json'
3
3
  require 'yaml'
4
4
 
5
- ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
6
- ActiveRecord::Base.establish_connection('test')
5
+ ActiveRecord::Base.configurations = {
6
+ 'test_sqlite3' => {adapter: 'sqlite3', database: "/tmp/typed_store.db"},
7
+ 'test_postgresql' => {adapter: 'postgresql', database: 'typed_store_test', username: 'postgres'},
8
+ 'test_mysql' => {adapter: 'mysql2', database: 'typed_store_test', username: 'travis'},
9
+ }
7
10
 
8
11
  def define_columns(t)
9
12
  t.integer :no_default
10
13
 
11
- t.string :name, default: ''
12
- t.string :email, null: true
14
+ t.string :name, default: '', null: false
15
+ t.string :email
13
16
 
14
- t.boolean :public, default: false
15
- t.boolean :enabled, default: true, null: true
17
+ t.boolean :public, default: false, null: false
18
+ t.boolean :enabled, default: true
16
19
 
17
- t.integer :age, default: 0
18
- t.integer :max_length, null: true
20
+ t.integer :age, default: 12, null: false
21
+ t.integer :max_length
19
22
 
20
- t.float :rate, default: 0
21
- t.float :price, null: true
23
+ t.float :rate, default: 0, null: false
24
+ t.float :price
22
25
 
23
- t.date :published_on, default: '1984-06-08'
24
- t.date :remind_on, null: true
26
+ t.date :published_on, default: '1984-06-08', null: false
27
+ t.date :remind_on
25
28
 
26
- t.datetime :published_at, default: '1984-06-08 13:57:12'
27
- t.datetime :remind_at, null: true
29
+ t.datetime :published_at, default: '1984-06-08 13:57:12', null: false
30
+ t.datetime :remind_at
28
31
 
29
- t.decimal :total_price, default: 4.2
30
- t.decimal :shipping_cost, null: true
32
+ t.decimal :total_price, default: 4.2, null: false, precision: 16, scale: 2
33
+ t.decimal :shipping_cost, precision: 16, scale: 2
31
34
 
35
+ t.integer :grades, array: true
36
+ t.string :tags, array: true, null: false, default: []
37
+
38
+ t.string :nickname, blank: false, default: 'Please enter your nickname'
39
+ end
40
+
41
+ def define_store_columns(t)
42
+ define_columns(t)
43
+ t.any :author
44
+ t.any :source, blank: false, default: 'web'
32
45
  end
33
46
 
34
47
  class CreateAllTables < ActiveRecord::Migration
48
+
49
+ def self.recreate_table(name, *args, &block)
50
+ execute "drop table if exists #{name}"
51
+ create_table(name, *args, &block)
52
+ end
53
+
35
54
  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 }
55
+ ActiveRecord::Base.establish_connection('test_mysql')
56
+ recreate_table(:mysql_regular_ar_models) { |t| define_columns(t); t.text :untyped_settings }
57
+
58
+ ActiveRecord::Base.establish_connection('test_postgresql')
59
+ recreate_table(:postgresql_regular_ar_models) { |t| define_columns(t); t.text :untyped_settings }
60
+
61
+ ActiveRecord::Base.establish_connection('test_sqlite3')
62
+ recreate_table(:sqlite3_regular_ar_models) { |t| define_columns(t); t.text :untyped_settings }
63
+ recreate_table(:yaml_typed_store_models) { |t| t.text :settings; t.text :untyped_settings }
64
+ recreate_table(:json_typed_store_models) { |t| t.text :settings; t.text :untyped_settings }
65
+ recreate_table(:marshal_typed_store_models) { |t| t.text :settings; t.text :untyped_settings }
40
66
  end
41
67
  end
42
68
  ActiveRecord::Migration.verbose = false
43
69
  CreateAllTables.up
44
70
 
45
- class RegularARModel < ActiveRecord::Base
71
+ class MysqlRegularARModel < ActiveRecord::Base
72
+ establish_connection 'test_mysql'
73
+ store :untyped_settings, accessors: [:title]
74
+ end
75
+
76
+ class PostgresqlRegularARModel < ActiveRecord::Base
77
+ establish_connection 'test_postgresql'
78
+ store :untyped_settings, accessors: [:title]
79
+ end
80
+
81
+ class Sqlite3RegularARModel < ActiveRecord::Base
82
+ establish_connection 'test_sqlite3'
83
+ store :untyped_settings, accessors: [:title]
46
84
  end
47
85
 
48
86
  class YamlTypedStoreModel < ActiveRecord::Base
87
+ establish_connection 'test_sqlite3'
88
+ store :untyped_settings, accessors: [:title]
49
89
  typed_store :settings do |s|
50
- define_columns(s)
90
+ define_store_columns(s)
51
91
  end
52
92
  end
53
93
 
@@ -69,13 +109,17 @@ class ColumnCoder
69
109
  end
70
110
 
71
111
  class JsonTypedStoreModel < ActiveRecord::Base
112
+ establish_connection 'test_sqlite3'
113
+ store :untyped_settings, accessors: [:title]
72
114
  typed_store :settings, coder: ColumnCoder.new(JSON) do |s|
73
- define_columns(s)
115
+ define_store_columns(s)
74
116
  end
75
117
  end
76
118
 
77
119
  class MarshalTypedStoreModel < ActiveRecord::Base
120
+ establish_connection 'test_sqlite3'
121
+ store :untyped_settings, accessors: [:title]
78
122
  typed_store :settings, coder: ColumnCoder.new(Marshal) do |s|
79
- define_columns(s)
123
+ define_store_columns(s)
80
124
  end
81
125
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-typedstore
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-27 00:00:00.000000000 Z
11
+ date: 2013-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: coveralls
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'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: sqlite3
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,34 @@ dependencies:
80
94
  - - '>='
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pg
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: mysql2
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
83
125
  - !ruby/object:Gem::Dependency
84
126
  name: database_cleaner
85
127
  requirement: !ruby/object:Gem::Requirement
@@ -113,6 +155,8 @@ files:
113
155
  - gemfiles/Gemfile.ar-4.0
114
156
  - gemfiles/Gemfile.ar-edge
115
157
  - lib/active_record/typed_store.rb
158
+ - lib/active_record/typed_store/ar_32_fallbacks.rb
159
+ - lib/active_record/typed_store/ar_41_fallbacks.rb
116
160
  - lib/active_record/typed_store/column.rb
117
161
  - lib/active_record/typed_store/dsl.rb
118
162
  - lib/active_record/typed_store/extension.rb