activerecord-typedstore 0.6.1 → 1.0.0.beta1
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 +4 -4
- data/.travis.yml +3 -14
- data/activerecord-typedstore.gemspec +5 -5
- data/gemfiles/Gemfile.ar-3.2 +1 -1
- data/gemfiles/Gemfile.ar-4.0 +1 -1
- data/gemfiles/Gemfile.ar-4.1 +1 -1
- data/gemfiles/Gemfile.ar-4.2 +1 -1
- data/gemfiles/Gemfile.ar-edge +3 -2
- data/lib/active_record/typed_store/dsl.rb +18 -16
- data/lib/active_record/typed_store/extension.rb +35 -137
- data/lib/active_record/typed_store/field.rb +76 -0
- data/lib/active_record/typed_store/type.rb +51 -0
- data/lib/active_record/typed_store/typed_hash.rb +13 -18
- data/lib/active_record/typed_store/version.rb +1 -1
- data/spec/active_record/typed_store/typed_hash_spec.rb +6 -1
- data/spec/active_record/typed_store_spec.rb +132 -73
- data/spec/spec_helper.rb +4 -2
- data/spec/support/models.rb +32 -8
- metadata +20 -21
- data/lib/active_record/typed_store/ar_32_fallbacks.rb +0 -95
- data/lib/active_record/typed_store/coder.rb +0 -28
- data/lib/active_record/typed_store/column.rb +0 -86
data/spec/spec_helper.rb
CHANGED
@@ -5,16 +5,18 @@ require 'database_cleaner'
|
|
5
5
|
|
6
6
|
require 'simplecov'
|
7
7
|
require 'coveralls'
|
8
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
8
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
|
9
9
|
SimpleCov::Formatter::HTMLFormatter,
|
10
10
|
Coveralls::SimpleCov::Formatter
|
11
|
-
]
|
11
|
+
])
|
12
12
|
SimpleCov.start
|
13
13
|
|
14
14
|
require 'activerecord-typedstore'
|
15
15
|
|
16
16
|
Dir[File.expand_path(File.join(File.dirname(__FILE__), 'support', '**', '*.rb'))].each { |f| require f }
|
17
17
|
|
18
|
+
Time.zone = 'UTC'
|
19
|
+
|
18
20
|
RSpec.configure do |config|
|
19
21
|
config.order = 'random'
|
20
22
|
end
|
data/spec/support/models.rb
CHANGED
@@ -40,7 +40,13 @@ def define_columns(t)
|
|
40
40
|
t.decimal :shipping_cost, precision: 16, scale: 2
|
41
41
|
|
42
42
|
t.integer :grades, array: true
|
43
|
-
|
43
|
+
|
44
|
+
if t.respond_to?(:name) && t.name =~ /sqlite|mysql/
|
45
|
+
# native sqlite cannot automatically cast array to yaml
|
46
|
+
t.string :tags, array: true, null: false, default: [].to_yaml
|
47
|
+
else
|
48
|
+
t.string :tags, array: true, null: false, default: []
|
49
|
+
end
|
44
50
|
|
45
51
|
t.string :nickname, blank: false, default: 'Please enter your nickname'
|
46
52
|
end
|
@@ -49,6 +55,7 @@ def define_store_columns(t)
|
|
49
55
|
define_columns(t)
|
50
56
|
t.any :author
|
51
57
|
t.any :source, blank: false, default: 'web'
|
58
|
+
t.any :signup, default: {}
|
52
59
|
t.string :country, blank: false, default: 'Canada', accessor: false
|
53
60
|
end
|
54
61
|
|
@@ -88,7 +95,9 @@ class CreateAllTables < ActiveRecord::Migration
|
|
88
95
|
end
|
89
96
|
end
|
90
97
|
ActiveRecord::Migration.verbose = true
|
91
|
-
|
98
|
+
ActiveRecord::Migration.suppress_messages do
|
99
|
+
CreateAllTables.up
|
100
|
+
end
|
92
101
|
|
93
102
|
class ColumnCoder
|
94
103
|
|
@@ -144,13 +153,29 @@ if ENV['POSTGRES']
|
|
144
153
|
end
|
145
154
|
|
146
155
|
if ENV['POSTGRES_JSON']
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
156
|
+
|
157
|
+
if AR_VERSION >= AR_4_2
|
158
|
+
|
159
|
+
class PostgresJsonTypedStoreModel < ActiveRecord::Base
|
160
|
+
establish_connection ENV['POSTGRES_URL'] || :test_postgresql
|
161
|
+
store :untyped_settings, accessors: [:title]
|
162
|
+
typed_store :settings, coder: false do |s|
|
163
|
+
define_store_columns(s)
|
164
|
+
end
|
152
165
|
end
|
166
|
+
|
167
|
+
else
|
168
|
+
|
169
|
+
class PostgresJsonTypedStoreModel < ActiveRecord::Base
|
170
|
+
establish_connection ENV['POSTGRES_URL'] || :test_postgresql
|
171
|
+
store :untyped_settings, accessors: [:title]
|
172
|
+
typed_store :settings, coder: ColumnCoder.new(AsJson) do |s|
|
173
|
+
define_store_columns(s)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
153
177
|
end
|
178
|
+
|
154
179
|
end
|
155
180
|
|
156
181
|
end
|
@@ -199,7 +224,6 @@ class MarshalTypedStoreModel < ActiveRecord::Base
|
|
199
224
|
end
|
200
225
|
end
|
201
226
|
|
202
|
-
|
203
227
|
Models = [
|
204
228
|
Sqlite3RegularARModel,
|
205
229
|
YamlTypedStoreModel,
|
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.
|
4
|
+
version: 1.0.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,20 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '4.2'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '5'
|
22
|
+
version: '5.1'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
29
|
+
version: '4.2'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '5'
|
32
|
+
version: '5.1'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: bundler
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,56 +92,56 @@ dependencies:
|
|
92
92
|
requirements:
|
93
93
|
- - "~>"
|
94
94
|
- !ruby/object:Gem::Version
|
95
|
-
version: '
|
95
|
+
version: '1'
|
96
96
|
type: :development
|
97
97
|
prerelease: false
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
100
|
- - "~>"
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
version: '
|
102
|
+
version: '1'
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
104
|
name: pg
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
107
|
- - "~>"
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version: '0'
|
109
|
+
version: '0.18'
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version: '0'
|
116
|
+
version: '0.18'
|
117
117
|
- !ruby/object:Gem::Dependency
|
118
118
|
name: mysql2
|
119
119
|
requirement: !ruby/object:Gem::Requirement
|
120
120
|
requirements:
|
121
|
-
- - "
|
121
|
+
- - ">"
|
122
122
|
- !ruby/object:Gem::Version
|
123
|
-
version: '0'
|
123
|
+
version: '0.3'
|
124
124
|
type: :development
|
125
125
|
prerelease: false
|
126
126
|
version_requirements: !ruby/object:Gem::Requirement
|
127
127
|
requirements:
|
128
|
-
- - "
|
128
|
+
- - ">"
|
129
129
|
- !ruby/object:Gem::Version
|
130
|
-
version: '0'
|
130
|
+
version: '0.3'
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
132
|
name: database_cleaner
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|
134
134
|
requirements:
|
135
135
|
- - "~>"
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
version: '
|
137
|
+
version: '1'
|
138
138
|
type: :development
|
139
139
|
prerelease: false
|
140
140
|
version_requirements: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: '
|
144
|
+
version: '1'
|
145
145
|
description: ActiveRecord::Store but with type definition
|
146
146
|
email:
|
147
147
|
- jean.boussier@gmail.com
|
@@ -163,11 +163,10 @@ files:
|
|
163
163
|
- gemfiles/Gemfile.ar-4.2
|
164
164
|
- gemfiles/Gemfile.ar-edge
|
165
165
|
- lib/active_record/typed_store.rb
|
166
|
-
- lib/active_record/typed_store/ar_32_fallbacks.rb
|
167
|
-
- lib/active_record/typed_store/coder.rb
|
168
|
-
- lib/active_record/typed_store/column.rb
|
169
166
|
- lib/active_record/typed_store/dsl.rb
|
170
167
|
- lib/active_record/typed_store/extension.rb
|
168
|
+
- lib/active_record/typed_store/field.rb
|
169
|
+
- lib/active_record/typed_store/type.rb
|
171
170
|
- lib/active_record/typed_store/typed_hash.rb
|
172
171
|
- lib/active_record/typed_store/version.rb
|
173
172
|
- lib/activerecord-typedstore.rb
|
@@ -191,9 +190,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
191
190
|
version: '0'
|
192
191
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
193
192
|
requirements:
|
194
|
-
- - "
|
193
|
+
- - ">"
|
195
194
|
- !ruby/object:Gem::Version
|
196
|
-
version:
|
195
|
+
version: 1.3.1
|
197
196
|
requirements: []
|
198
197
|
rubyforge_project:
|
199
198
|
rubygems_version: 2.4.6
|
@@ -1,95 +0,0 @@
|
|
1
|
-
class ActiveRecord::Store::IndifferentCoder
|
2
|
-
# Backport from rails 4.0
|
3
|
-
def initialize(coder_or_class_name)
|
4
|
-
@coder =
|
5
|
-
if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
|
6
|
-
coder_or_class_name
|
7
|
-
else
|
8
|
-
ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def dump(obj)
|
13
|
-
@coder.dump self.class.as_indifferent_hash(obj)
|
14
|
-
end
|
15
|
-
|
16
|
-
def load(yaml)
|
17
|
-
self.class.as_indifferent_hash @coder.load(yaml)
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
module ActiveRecord::TypedStore
|
23
|
-
|
24
|
-
module AR32Fallbacks
|
25
|
-
extend ActiveSupport::Concern
|
26
|
-
|
27
|
-
included do
|
28
|
-
cattr_accessor :virtual_attribute_methods
|
29
|
-
self.virtual_attribute_methods = []
|
30
|
-
end
|
31
|
-
|
32
|
-
module ClassMethods
|
33
|
-
|
34
|
-
def typed_store(store_attribute, dsl)
|
35
|
-
_ar_32_fallback_accessors(store_attribute, dsl.accessors)
|
36
|
-
end
|
37
|
-
|
38
|
-
protected
|
39
|
-
|
40
|
-
def define_virtual_attribute_method(name)
|
41
|
-
virtual_attribute_methods << name
|
42
|
-
define_attribute_method(name)
|
43
|
-
end
|
44
|
-
|
45
|
-
# ActiveModel override heavilly inspired from the original code
|
46
|
-
def define_attribute_method(attr_name)
|
47
|
-
return super unless virtual_attribute_methods.include?(attr_name)
|
48
|
-
|
49
|
-
attribute_method_matchers.each do |matcher|
|
50
|
-
method_name = matcher.method_name(attr_name)
|
51
|
-
unless instance_method_already_implemented?(method_name)
|
52
|
-
define_optimized_call generated_attribute_methods, method_name, matcher.method_missing_target, attr_name.to_s
|
53
|
-
end
|
54
|
-
end
|
55
|
-
attribute_method_matchers_cache.clear
|
56
|
-
end
|
57
|
-
|
58
|
-
def _ar_32_fallback_accessors(store_attribute, accessors)
|
59
|
-
accessors.each do |accessor|
|
60
|
-
_ar_32_fallback_accessor(store_attribute, accessor)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def _ar_32_fallback_accessor(store_attribute, key)
|
65
|
-
define_method("#{key}=") do |value|
|
66
|
-
write_store_attribute(store_attribute, key, value)
|
67
|
-
end
|
68
|
-
define_method(key) do
|
69
|
-
read_store_attribute(store_attribute, key)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
def initialize_store_attribute(store_attribute)
|
78
|
-
send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash)
|
79
|
-
send(store_attribute)
|
80
|
-
end
|
81
|
-
|
82
|
-
def read_store_attribute(store_attribute, key)
|
83
|
-
store = initialize_store_attribute(store_attribute)
|
84
|
-
store[key]
|
85
|
-
end
|
86
|
-
|
87
|
-
def write_store_attribute(store_attribute, key, value)
|
88
|
-
attribute_will_change!(store_attribute.to_s)
|
89
|
-
send(store_attribute)[key] = value
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
Extension.send(:include, AR32Fallbacks)
|
95
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module ActiveRecord::TypedStore
|
2
|
-
|
3
|
-
class Coder < ::ActiveRecord::Store::IndifferentCoder
|
4
|
-
|
5
|
-
class << self
|
6
|
-
|
7
|
-
def create(store_class)
|
8
|
-
Class.new(self) do
|
9
|
-
@store_class = store_class
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def as_indifferent_hash(obj)
|
14
|
-
return obj if obj.is_a?(@store_class)
|
15
|
-
@store_class.new(obj)
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
delegate :as_indifferent_hash, to: 'self.class'
|
21
|
-
|
22
|
-
def dump(obj)
|
23
|
-
@coder.dump(obj.try(:to_hash) || {})
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
module ActiveRecord::TypedStore
|
2
|
-
|
3
|
-
class Column < ::ActiveRecord::ConnectionAdapters::Column
|
4
|
-
attr_reader :array, :blank
|
5
|
-
|
6
|
-
def initialize(name, type, options={})
|
7
|
-
@name = name
|
8
|
-
@type = type
|
9
|
-
@array = options.fetch(:array, false)
|
10
|
-
@default = extract_default(options.fetch(:default, nil))
|
11
|
-
@null = options.fetch(:null, true)
|
12
|
-
@blank = options.fetch(:blank, true)
|
13
|
-
@accessor = options.fetch(:accessor, true)
|
14
|
-
@scale = options[:scale]
|
15
|
-
@limit = options[:limit]
|
16
|
-
@precision = options[:precision]
|
17
|
-
end
|
18
|
-
|
19
|
-
def accessor?
|
20
|
-
@accessor
|
21
|
-
end
|
22
|
-
|
23
|
-
def cast(value)
|
24
|
-
casted_value = type_cast(value)
|
25
|
-
if !blank
|
26
|
-
casted_value = default if casted_value.blank?
|
27
|
-
elsif !null
|
28
|
-
casted_value = default if casted_value.nil?
|
29
|
-
end
|
30
|
-
casted_value
|
31
|
-
end
|
32
|
-
|
33
|
-
def extract_default(value)
|
34
|
-
return value if (type == :string || type == :text) && value.nil?
|
35
|
-
|
36
|
-
type_cast(value)
|
37
|
-
end
|
38
|
-
|
39
|
-
def type_cast(value, map=true)
|
40
|
-
if array && (map || value.is_a?(Array))
|
41
|
-
return [] if map && !value.is_a?(Array)
|
42
|
-
return value.map{ |v| type_cast(v, false) }
|
43
|
-
end
|
44
|
-
|
45
|
-
if type == :string || type == :text
|
46
|
-
return value.to_s unless value.nil? && (null || array)
|
47
|
-
end
|
48
|
-
|
49
|
-
if IS_AR_3_2 && type == :datetime && value.is_a?(DateTime)
|
50
|
-
return super(value.iso8601)
|
51
|
-
end
|
52
|
-
|
53
|
-
defined?(super) ? super(value) : type_cast_from_database(value)
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
57
|
-
|
58
|
-
if defined? ::ActiveRecord::Type
|
59
|
-
BaseColumn = remove_const(:Column)
|
60
|
-
|
61
|
-
class DecimalType < ::ActiveRecord::Type::Decimal
|
62
|
-
def type_cast_from_database(value)
|
63
|
-
value = value.to_s if value.is_a?(Float)
|
64
|
-
super(value)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
class Column < BaseColumn
|
69
|
-
CAST_TYPES = {
|
70
|
-
boolean: ::ActiveRecord::Type::Boolean,
|
71
|
-
integer: ::ActiveRecord::Type::Integer,
|
72
|
-
string: ::ActiveRecord::Type::String,
|
73
|
-
float: ::ActiveRecord::Type::Float,
|
74
|
-
date: ::ActiveRecord::Type::Date,
|
75
|
-
datetime: ::ActiveRecord::Type::DateTime,
|
76
|
-
decimal: DecimalType,
|
77
|
-
any: ::ActiveRecord::Type::Value,
|
78
|
-
}
|
79
|
-
|
80
|
-
def initialize(_, type, options = {})
|
81
|
-
@cast_type = CAST_TYPES.fetch(type).new(options.slice(:limit, :scale, :precision))
|
82
|
-
super
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|