store_attribute 0.4.1 → 0.5.3
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 +5 -5
- data/.rubocop.yml +35 -32
- data/.travis.yml +9 -2
- data/Gemfile +1 -1
- data/README.md +17 -11
- data/Rakefile +6 -2
- data/gemfiles/{rails42.gemfile → rails5.gemfile} +1 -1
- data/gemfiles/rails6.gemfile +5 -0
- data/gemfiles/{rails-edge.gemfile → railsmaster.gemfile} +0 -2
- data/lib/store_attribute.rb +4 -2
- data/lib/store_attribute/active_record.rb +3 -1
- data/lib/store_attribute/active_record/store.rb +9 -4
- data/lib/store_attribute/active_record/type/typed_store.rb +11 -24
- data/lib/store_attribute/version.rb +3 -1
- data/spec/cases/store_attribute_spec.rb +35 -33
- data/spec/spec_helper.rb +19 -16
- data/spec/store_attribute/typed_store_spec.rb +52 -37
- data/spec/support/money_type.rb +6 -4
- data/spec/support/user.rb +3 -1
- data/store_attribute.gemspec +11 -4
- metadata +58 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 26bfe00fc2e06215356a2e050700f0d257e56401249c97487e26f58133f4ea0e
|
4
|
+
data.tar.gz: 5e9ba6def76cf0f0aa77130db602929da9725f1483508e244557e90e8bd22892
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3de11ae80f51e606826b3dbbdd92afa4a7443ec24ef595c616908adb23f2588908052f5db52d963164843aa0868c30747b54921e9fdac2b7deaede4b770413b3
|
7
|
+
data.tar.gz: 6032541f1dd51712f9755b9ca8ccfa25b145c670d319a3dde909917fb0b7447069600a3fdd7c8fce40e131733fa6495e7be420f7a77aa5f60a88c36c28301f3a
|
data/.rubocop.yml
CHANGED
@@ -1,51 +1,54 @@
|
|
1
|
+
require:
|
2
|
+
- standard/cop/semantic_blocks
|
3
|
+
- rubocop-md
|
4
|
+
|
5
|
+
inherit_gem:
|
6
|
+
standard: config/base.yml
|
7
|
+
|
1
8
|
AllCops:
|
2
|
-
# Include gemspec and Rakefile
|
3
|
-
Include:
|
4
|
-
- 'lib/**/*.rb'
|
5
|
-
- 'lib/**/*.rake'
|
6
|
-
- 'spec/**/*.rb'
|
7
9
|
Exclude:
|
8
|
-
- 'bin
|
9
|
-
- 'spec/dummy/**/*'
|
10
|
+
- 'bin/*'
|
10
11
|
- 'tmp/**/*'
|
12
|
+
- 'Gemfile'
|
13
|
+
- 'vendor/**/*'
|
14
|
+
- 'gemfiles/**/*'
|
11
15
|
- 'bench/**/*'
|
12
16
|
DisplayCopNames: true
|
13
|
-
|
17
|
+
TargetRubyVersion: 2.4
|
14
18
|
|
15
|
-
|
19
|
+
Standard/SemanticBlocks:
|
16
20
|
Enabled: false
|
17
21
|
|
18
|
-
Style/
|
19
|
-
Enabled:
|
22
|
+
Style/FrozenStringLiteralComment:
|
23
|
+
Enabled: true
|
20
24
|
|
21
|
-
Style/
|
22
|
-
|
23
|
-
- 'spec/**/*.rb'
|
25
|
+
Style/TrailingCommaInArrayLiteral:
|
26
|
+
EnforcedStyleForMultiline: no_comma
|
24
27
|
|
25
|
-
Style/
|
26
|
-
|
28
|
+
Style/TrailingCommaInHashLiteral:
|
29
|
+
EnforcedStyleForMultiline: no_comma
|
27
30
|
|
28
|
-
|
29
|
-
EnforcedStyle:
|
31
|
+
Layout/AlignParameters:
|
32
|
+
EnforcedStyle: with_first_parameter
|
30
33
|
|
31
|
-
|
34
|
+
Lint/Void:
|
32
35
|
Exclude:
|
33
|
-
- '
|
36
|
+
- '**/*.md'
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
Metrics/MethodLength:
|
38
|
+
# See https://github.com/rubocop-hq/rubocop/issues/4222
|
39
|
+
Lint/AmbiguousBlockAssociation:
|
39
40
|
Exclude:
|
40
|
-
- 'spec
|
41
|
+
- 'spec/**/*'
|
42
|
+
- '**/*.md'
|
41
43
|
|
42
|
-
|
43
|
-
Max: 100
|
44
|
+
Lint/DuplicateMethods:
|
44
45
|
Exclude:
|
45
|
-
- '
|
46
|
+
- '**/*.md'
|
46
47
|
|
47
|
-
|
48
|
-
|
48
|
+
Naming/FileName:
|
49
|
+
Exclude:
|
50
|
+
- '**/*.md'
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
+
Layout/InitialIndentation:
|
53
|
+
Exclude:
|
54
|
+
- 'CHANGELOG.md'
|
data/.travis.yml
CHANGED
@@ -10,5 +10,12 @@ before_script:
|
|
10
10
|
|
11
11
|
matrix:
|
12
12
|
include:
|
13
|
-
- rvm: 2.
|
14
|
-
gemfile: gemfiles/
|
13
|
+
- rvm: 2.4.1
|
14
|
+
gemfile: gemfiles/rails5.gemfile
|
15
|
+
- rvm: 2.6.2
|
16
|
+
gemfile: gemfiles/rails6.gemfile
|
17
|
+
- rvm: ruby-head
|
18
|
+
gemfile: gemfiles/railsmaster.gemfile
|
19
|
+
allow_failures:
|
20
|
+
- rvm: ruby-head
|
21
|
+
gemfile: gemfiles/railsmaster.gemfile
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,15 +4,21 @@
|
|
4
4
|
|
5
5
|
ActiveRecord extension which adds typecasting to store accessors.
|
6
6
|
|
7
|
-
Compatible with
|
7
|
+
Compatible with Rails 4.2 and Rails 5+.
|
8
8
|
|
9
|
+
<a href="https://evilmartians.com/">
|
10
|
+
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
9
11
|
|
10
12
|
### Install
|
11
13
|
|
12
14
|
In your Gemfile:
|
13
15
|
|
14
16
|
```ruby
|
15
|
-
|
17
|
+
# for Rails 5+ (6 is supported)
|
18
|
+
gem "store_attribute", "~> 0.5.0"
|
19
|
+
|
20
|
+
# for Rails 4.2
|
21
|
+
gem "store_attribute", "~> 0.4.0"
|
16
22
|
```
|
17
23
|
|
18
24
|
### Usage
|
@@ -20,14 +26,14 @@ gem "store_attribute", "~>0.4.0" # version 0.4.x is for Rails 4.2.x and 0.5.x is
|
|
20
26
|
You can use `store_attribute` method to add additional accessors with a type to an existing store on a model.
|
21
27
|
|
22
28
|
```ruby
|
23
|
-
|
29
|
+
store_attribute(store_name, name, type, options)
|
24
30
|
```
|
25
31
|
|
26
32
|
Where:
|
27
33
|
- `store_name` The name of the store.
|
28
34
|
- `name` The name of the accessor to the store.
|
29
35
|
- `type` A symbol such as `:string` or `:integer`, or a type object to be used for the accessor.
|
30
|
-
- `options` A hash of cast type options such as `precision`, `limit`, `scale`.
|
36
|
+
- `options` (optional) A hash of cast type options such as `precision`, `limit`, `scale`.
|
31
37
|
|
32
38
|
Type casting occurs every time you write data through accessor or update store itself
|
33
39
|
and when object is loaded from database.
|
@@ -43,10 +49,10 @@ class MegaUser < User
|
|
43
49
|
store_attribute :settings, :active, :boolean
|
44
50
|
end
|
45
51
|
|
46
|
-
u = MegaUser.new(active: false, login_at:
|
52
|
+
u = MegaUser.new(active: false, login_at: "2015-01-01 00:01", ratio: "63.4608")
|
47
53
|
|
48
54
|
u.login_at.is_a?(DateTime) # => true
|
49
|
-
u.login_at = DateTime.new(2015,1,1,11,0,0)
|
55
|
+
u.login_at = DateTime.new(2015, 1, 1, 11, 0, 0)
|
50
56
|
u.ratio # => 63
|
51
57
|
u.active # => false
|
52
58
|
# And we also have a predicate method
|
@@ -54,17 +60,17 @@ u.active? # => false
|
|
54
60
|
u.reload
|
55
61
|
|
56
62
|
# After loading record from db store contains casted data
|
57
|
-
u.settings[
|
63
|
+
u.settings["login_at"] == DateTime.new(2015, 1, 1, 11, 0, 0) # => true
|
58
64
|
|
59
65
|
# If you update store explicitly then the value returned
|
60
66
|
# by accessor isn't type casted
|
61
|
-
u.settings[
|
67
|
+
u.settings["ratio"] = "3.141592653"
|
62
68
|
u.ratio # => "3.141592653"
|
63
69
|
|
64
70
|
# On the other hand, writing through accessor set correct data within store
|
65
|
-
u.ratio = "3.
|
71
|
+
u.ratio = "3.141592653"
|
66
72
|
u.ratio # => 3
|
67
|
-
u.settings[
|
73
|
+
u.settings["ratio"] # => 3
|
68
74
|
```
|
69
75
|
|
70
76
|
You can also specify type using usual `store_accessor` method:
|
@@ -81,4 +87,4 @@ Or through `store`:
|
|
81
87
|
class User < ActiveRecord::Base
|
82
88
|
store :settings, accessors: [:color, :homepage, login_at: :datetime], coder: JSON
|
83
89
|
end
|
84
|
-
```
|
90
|
+
```
|
data/Rakefile
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "bundler/gem_tasks"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
require "rubocop/rake_task"
|
2
6
|
|
3
|
-
require 'rspec/core/rake_task'
|
4
7
|
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
RuboCop::RakeTask.new
|
5
9
|
|
6
|
-
task :
|
10
|
+
task default: [:rubocop, :spec]
|
data/lib/store_attribute.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/store"
|
4
|
+
require "store_attribute/active_record/type/typed_store"
|
3
5
|
|
4
6
|
module ActiveRecord
|
5
7
|
module Store
|
6
8
|
module ClassMethods # :nodoc:
|
9
|
+
alias _orig_store store
|
7
10
|
# Defines store on this model.
|
8
11
|
#
|
9
12
|
# +store_name+ The name of the store.
|
@@ -21,9 +24,11 @@ module ActiveRecord
|
|
21
24
|
# store :settings, accessors: [:color, :homepage, login_at: :datetime], coder: JSON
|
22
25
|
# end
|
23
26
|
def store(store_name, options = {})
|
24
|
-
|
25
|
-
|
27
|
+
accessors = options.delete(:accessors)
|
28
|
+
_orig_store(store_name, options)
|
29
|
+
store_accessor(store_name, *accessors) if accessors
|
26
30
|
end
|
31
|
+
|
27
32
|
# Adds additional accessors to an existing store on this model.
|
28
33
|
#
|
29
34
|
# +store_name+ The name of the store.
|
@@ -1,22 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/type"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module Type # :nodoc:
|
5
|
-
BASE_TYPES = {
|
6
|
-
boolean: ::ActiveRecord::Type::Boolean,
|
7
|
-
integer: ::ActiveRecord::Type::Integer,
|
8
|
-
string: ::ActiveRecord::Type::String,
|
9
|
-
float: ::ActiveRecord::Type::Float,
|
10
|
-
date: ::ActiveRecord::Type::Date,
|
11
|
-
datetime: ::ActiveRecord::Type::DateTime,
|
12
|
-
decimal: ::ActiveRecord::Type::Decimal
|
13
|
-
}.freeze
|
14
|
-
|
15
|
-
def self.lookup_type(type, options)
|
16
|
-
BASE_TYPES[type.to_sym].try(:new, options) ||
|
17
|
-
ActiveRecord::Base.connection.type_map.lookup(type.to_s, options)
|
18
|
-
end
|
19
|
-
|
20
7
|
class TypedStore < DelegateClass(ActiveRecord::Type::Value) # :nodoc:
|
21
8
|
# Creates +TypedStore+ type instance and specifies type caster
|
22
9
|
# for key.
|
@@ -33,26 +20,26 @@ module ActiveRecord
|
|
33
20
|
end
|
34
21
|
|
35
22
|
def add_typed_key(key, type, **options)
|
36
|
-
type = Type.
|
23
|
+
type = ActiveRecord::Type.lookup(type, options) if type.is_a?(Symbol)
|
37
24
|
@accessor_types[key.to_s] = type
|
38
25
|
end
|
39
26
|
|
40
|
-
def
|
27
|
+
def deserialize(value)
|
41
28
|
hash = super
|
42
29
|
if hash
|
43
30
|
accessor_types.each do |key, type|
|
44
|
-
hash[key] = type.
|
31
|
+
hash[key] = type.deserialize(hash[key]) if hash.key?(key)
|
45
32
|
end
|
46
33
|
end
|
47
34
|
hash
|
48
35
|
end
|
49
36
|
|
50
|
-
def
|
37
|
+
def serialize(value)
|
51
38
|
if value.is_a?(Hash)
|
52
39
|
typed_casted = {}
|
53
40
|
accessor_types.each do |key, type|
|
54
41
|
k = key_to_cast(value, key)
|
55
|
-
typed_casted[k] = type.
|
42
|
+
typed_casted[k] = type.serialize(value[k]) unless k.nil?
|
56
43
|
end
|
57
44
|
super(value.merge(typed_casted))
|
58
45
|
else
|
@@ -60,11 +47,11 @@ module ActiveRecord
|
|
60
47
|
end
|
61
48
|
end
|
62
49
|
|
63
|
-
def
|
50
|
+
def cast(value)
|
64
51
|
hash = super
|
65
52
|
if hash
|
66
53
|
accessor_types.each do |key, type|
|
67
|
-
hash[key] = type.
|
54
|
+
hash[key] = type.cast(hash[key]) if hash.key?(key)
|
68
55
|
end
|
69
56
|
end
|
70
57
|
hash
|
@@ -75,7 +62,7 @@ module ActiveRecord
|
|
75
62
|
end
|
76
63
|
|
77
64
|
def write(object, attribute, key, value)
|
78
|
-
value = type_for(key).
|
65
|
+
value = type_for(key).cast(value) if typed?(key)
|
79
66
|
store_accessor.write(object, attribute, key, value)
|
80
67
|
end
|
81
68
|
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
2
4
|
|
3
5
|
describe StoreAttribute do
|
4
6
|
before do
|
5
7
|
@connection = ActiveRecord::Base.connection
|
6
8
|
|
7
9
|
@connection.transaction do
|
8
|
-
@connection.create_table(
|
10
|
+
@connection.create_table("users") do |t|
|
9
11
|
t.jsonb :jparams, default: {}, null: false
|
10
12
|
t.text :custom
|
11
13
|
t.hstore :hdata, default: {}, null: false
|
@@ -16,23 +18,23 @@ describe StoreAttribute do
|
|
16
18
|
end
|
17
19
|
|
18
20
|
after do
|
19
|
-
@connection.drop_table
|
21
|
+
@connection.drop_table "users", if_exists: true
|
20
22
|
end
|
21
23
|
|
22
24
|
let(:time) { DateTime.new(2015, 2, 14, 17, 0, 0) }
|
23
|
-
let(:time_str) {
|
24
|
-
let(:time_str_utc) {
|
25
|
+
let(:time_str) { "2015-02-14 17:00" }
|
26
|
+
let(:time_str_utc) { "2015-02-14 17:00:00 UTC" }
|
25
27
|
|
26
28
|
context "hstore" do
|
27
29
|
it "typecasts on build" do
|
28
|
-
user = User.new(visible:
|
30
|
+
user = User.new(visible: "t", login_at: time_str)
|
29
31
|
expect(user.visible).to eq true
|
30
32
|
expect(user).to be_visible
|
31
33
|
expect(user.login_at).to eq time
|
32
34
|
end
|
33
35
|
|
34
36
|
it "typecasts on reload" do
|
35
|
-
user = User.new(visible:
|
37
|
+
user = User.new(visible: "t", login_at: time_str)
|
36
38
|
user.save!
|
37
39
|
user = User.find(user.id)
|
38
40
|
|
@@ -54,8 +56,8 @@ describe StoreAttribute do
|
|
54
56
|
expect(user.login_at).to eq time
|
55
57
|
|
56
58
|
ron = RawUser.find(user.id)
|
57
|
-
expect(ron.hdata[
|
58
|
-
expect(ron.hdata[
|
59
|
+
expect(ron.hdata["visible"]).to eq "false"
|
60
|
+
expect(ron.hdata["login_at"]).to eq time_str_utc
|
59
61
|
end
|
60
62
|
|
61
63
|
it "handles options" do
|
@@ -63,8 +65,8 @@ describe StoreAttribute do
|
|
63
65
|
end
|
64
66
|
|
65
67
|
it "YAML roundtrip" do
|
66
|
-
user = User.create!(visible:
|
67
|
-
dumped = YAML.load(YAML.dump(user))
|
68
|
+
user = User.create!(visible: "0", login_at: time_str)
|
69
|
+
dumped = YAML.load(YAML.dump(user)) # rubocop:disable Security/YAMLLoad
|
68
70
|
|
69
71
|
expect(dumped.visible).to be false
|
70
72
|
expect(dumped.login_at).to eq time
|
@@ -74,26 +76,26 @@ describe StoreAttribute do
|
|
74
76
|
context "jsonb" do
|
75
77
|
it "typecasts on build" do
|
76
78
|
jamie = User.new(
|
77
|
-
active:
|
79
|
+
active: "true",
|
78
80
|
salary: 3.1999,
|
79
|
-
birthday:
|
81
|
+
birthday: "2000-01-01"
|
80
82
|
)
|
81
83
|
expect(jamie).to be_active
|
82
84
|
expect(jamie.salary).to eq 3
|
83
85
|
expect(jamie.birthday).to eq Date.new(2000, 1, 1)
|
84
|
-
expect(jamie.jparams[
|
85
|
-
expect(jamie.jparams[
|
86
|
+
expect(jamie.jparams["birthday"]).to eq Date.new(2000, 1, 1)
|
87
|
+
expect(jamie.jparams["active"]).to eq true
|
86
88
|
end
|
87
89
|
|
88
90
|
it "typecasts on reload" do
|
89
|
-
jamie = User.create!(jparams: {
|
91
|
+
jamie = User.create!(jparams: {"active" => "1", "birthday" => "01/01/2000", "salary" => "3.14"})
|
90
92
|
jamie = User.find(jamie.id)
|
91
93
|
|
92
94
|
expect(jamie).to be_active
|
93
95
|
expect(jamie.salary).to eq 3
|
94
96
|
expect(jamie.birthday).to eq Date.new(2000, 1, 1)
|
95
|
-
expect(jamie.jparams[
|
96
|
-
expect(jamie.jparams[
|
97
|
+
expect(jamie.jparams["birthday"]).to eq Date.new(2000, 1, 1)
|
98
|
+
expect(jamie.jparams["active"]).to eq true
|
97
99
|
end
|
98
100
|
|
99
101
|
it "works with accessors" do
|
@@ -101,9 +103,9 @@ describe StoreAttribute do
|
|
101
103
|
john.active = 1
|
102
104
|
|
103
105
|
expect(john).to be_active
|
104
|
-
expect(john.jparams[
|
106
|
+
expect(john.jparams["active"]).to eq true
|
105
107
|
|
106
|
-
john.jparams = {
|
108
|
+
john.jparams = {active: "true", salary: "123.123", birthday: "01/01/2012"}
|
107
109
|
expect(john).to be_active
|
108
110
|
expect(john.birthday).to eq Date.new(2012, 1, 1)
|
109
111
|
expect(john.salary).to eq 123
|
@@ -111,9 +113,9 @@ describe StoreAttribute do
|
|
111
113
|
john.save!
|
112
114
|
|
113
115
|
ron = RawUser.find(john.id)
|
114
|
-
expect(ron.jparams[
|
115
|
-
expect(ron.jparams[
|
116
|
-
expect(ron.jparams[
|
116
|
+
expect(ron.jparams["active"]).to eq true
|
117
|
+
expect(ron.jparams["birthday"]).to eq "2012-01-01"
|
118
|
+
expect(ron.jparams["salary"]).to eq 123
|
117
119
|
end
|
118
120
|
|
119
121
|
it "re-typecast old data" do
|
@@ -127,8 +129,8 @@ describe StoreAttribute do
|
|
127
129
|
jamie.save!
|
128
130
|
|
129
131
|
ron = RawUser.find(jamie.id)
|
130
|
-
expect(ron.jparams[
|
131
|
-
expect(ron.jparams[
|
132
|
+
expect(ron.jparams["active"]).to eq true
|
133
|
+
expect(ron.jparams["salary"]).to eq 12
|
132
134
|
end
|
133
135
|
end
|
134
136
|
|
@@ -139,7 +141,7 @@ describe StoreAttribute do
|
|
139
141
|
end
|
140
142
|
|
141
143
|
it "typecasts on reload" do
|
142
|
-
jamie = User.create!(custom: {
|
144
|
+
jamie = User.create!(custom: {price: "$12"})
|
143
145
|
expect(jamie.reload.price).to eq 1200
|
144
146
|
|
145
147
|
jamie = User.find(jamie.id)
|
@@ -150,22 +152,22 @@ describe StoreAttribute do
|
|
150
152
|
|
151
153
|
context "store subtype" do
|
152
154
|
it "typecasts on build" do
|
153
|
-
user = User.new(inner_json: {
|
154
|
-
expect(user.inner_json).to eq(
|
155
|
+
user = User.new(inner_json: {x: 1})
|
156
|
+
expect(user.inner_json).to eq("x" => 1)
|
155
157
|
end
|
156
158
|
|
157
159
|
it "typecasts on update" do
|
158
160
|
user = User.new
|
159
|
-
user.update!(inner_json: {
|
160
|
-
expect(user.inner_json).to eq(
|
161
|
+
user.update!(inner_json: {x: 1})
|
162
|
+
expect(user.inner_json).to eq("x" => 1)
|
161
163
|
|
162
|
-
expect(user.reload.inner_json).to eq(
|
164
|
+
expect(user.reload.inner_json).to eq("x" => 1)
|
163
165
|
end
|
164
166
|
|
165
167
|
it "typecasts on reload" do
|
166
|
-
jamie = User.create!(inner_json: {
|
168
|
+
jamie = User.create!(inner_json: {x: 1})
|
167
169
|
jamie = User.find(jamie.id)
|
168
|
-
expect(jamie.inner_json).to eq(
|
170
|
+
expect(jamie.inner_json).to eq("x" => 1)
|
169
171
|
end
|
170
172
|
end
|
171
173
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,26 +1,30 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
2
4
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
5
|
|
4
|
-
if ENV[
|
5
|
-
require
|
6
|
-
SimpleCov.root File.join(File.dirname(__FILE__),
|
6
|
+
if ENV["COVER"]
|
7
|
+
require "simplecov"
|
8
|
+
SimpleCov.root File.join(File.dirname(__FILE__), "..")
|
7
9
|
SimpleCov.start
|
8
10
|
end
|
9
11
|
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
12
|
+
require "rspec"
|
13
|
+
require "pry-byebug"
|
14
|
+
require "active_record"
|
15
|
+
require "pg"
|
16
|
+
require "store_attribute"
|
17
|
+
|
18
|
+
RAILS_5_1 = ActiveRecord.version.release >= Gem::Version.new("5.1.0")
|
15
19
|
|
16
20
|
ActiveRecord::Base.establish_connection(
|
17
|
-
adapter:
|
18
|
-
database:
|
21
|
+
adapter: "postgresql",
|
22
|
+
database: "store_attribute_test"
|
19
23
|
)
|
20
24
|
connection = ActiveRecord::Base.connection
|
21
25
|
|
22
|
-
unless connection.extension_enabled?(
|
23
|
-
connection.enable_extension
|
26
|
+
unless connection.extension_enabled?("hstore")
|
27
|
+
connection.enable_extension "hstore"
|
24
28
|
connection.commit_db_transaction
|
25
29
|
end
|
26
30
|
|
@@ -31,12 +35,11 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
|
31
35
|
RSpec.configure do |config|
|
32
36
|
config.mock_with :rspec
|
33
37
|
|
34
|
-
config.
|
35
|
-
config.run_all_when_everything_filtered = true
|
38
|
+
config.filter_run_when_matching :focus
|
36
39
|
|
37
40
|
config.example_status_persistence_file_path = "tmp/rspec_examples.txt"
|
38
41
|
|
39
42
|
if config.files_to_run.one?
|
40
|
-
config.default_formatter =
|
43
|
+
config.default_formatter = "doc"
|
41
44
|
end
|
42
45
|
end
|
@@ -1,94 +1,109 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
2
4
|
|
3
5
|
describe ActiveRecord::Type::TypedStore do
|
4
6
|
let(:json_type) { ActiveRecord::Type::Serialized.new(ActiveRecord::Type::Text.new, ActiveRecord::Coders::JSON) }
|
5
|
-
let(:yaml_type) do
|
6
|
-
ActiveRecord::Type::Serialized.new(
|
7
|
-
ActiveRecord::Type::Text.new,
|
8
|
-
ActiveRecord::Store::IndifferentCoder.new(
|
9
|
-
ActiveRecord::Coders::YAMLColumn.new(Hash)
|
10
|
-
)
|
11
|
-
)
|
12
|
-
end
|
13
7
|
|
14
8
|
context "with json store" do
|
15
9
|
subject { described_class.new(json_type) }
|
16
10
|
|
17
|
-
describe "#
|
11
|
+
describe "#cast" do
|
18
12
|
it "without key types", :aggregate_failures do
|
19
|
-
expect(subject.
|
20
|
-
expect(subject.
|
13
|
+
expect(subject.cast([1, 2])).to eq [1, 2]
|
14
|
+
expect(subject.cast("a" => "b")).to eq("a" => "b")
|
21
15
|
end
|
22
16
|
|
23
17
|
it "with type keys" do
|
24
|
-
subject.add_typed_key(
|
18
|
+
subject.add_typed_key("date", :date)
|
25
19
|
|
26
20
|
date = ::Date.new(2016, 6, 22)
|
27
|
-
expect(subject.
|
21
|
+
expect(subject.cast(date: "2016-06-22")).to eq("date" => date)
|
28
22
|
end
|
29
23
|
end
|
30
24
|
|
31
|
-
describe "#
|
25
|
+
describe "#deserialize" do
|
32
26
|
it "without key types", :aggregate_failures do
|
33
|
-
expect(subject.
|
34
|
-
expect(subject.
|
27
|
+
expect(subject.deserialize("[1,2]")).to eq [1, 2]
|
28
|
+
expect(subject.deserialize('{"a":"b"}')).to eq("a" => "b")
|
35
29
|
end
|
36
30
|
|
37
31
|
it "with type keys" do
|
38
|
-
subject.add_typed_key(
|
32
|
+
subject.add_typed_key("date", :date)
|
39
33
|
|
40
34
|
date = ::Date.new(2016, 6, 22)
|
41
|
-
expect(subject.
|
35
|
+
expect(subject.deserialize('{"date":"2016-06-22"}')).to eq("date" => date)
|
42
36
|
end
|
43
37
|
end
|
44
38
|
|
45
|
-
describe "#
|
39
|
+
describe "#serialize" do
|
46
40
|
it "without key types", :aggregate_failures do
|
47
|
-
expect(subject.
|
48
|
-
expect(subject.
|
41
|
+
expect(subject.serialize([1, 2])).to eq "[1,2]"
|
42
|
+
expect(subject.serialize("a" => "b")).to eq '{"a":"b"}'
|
49
43
|
end
|
50
44
|
|
51
45
|
it "with type keys" do
|
52
|
-
subject.add_typed_key(
|
46
|
+
subject.add_typed_key("date", :date)
|
53
47
|
|
54
48
|
date = ::Date.new(2016, 6, 22)
|
55
|
-
expect(subject.
|
49
|
+
expect(subject.serialize(date: date)).to eq '{"date":"2016-06-22"}'
|
56
50
|
end
|
57
51
|
|
58
52
|
it "with type key with option" do
|
59
|
-
subject.add_typed_key(
|
53
|
+
subject.add_typed_key("val", :integer, limit: 1)
|
60
54
|
|
61
|
-
expect { subject.
|
55
|
+
expect { subject.serialize(val: 1024) }.to raise_error(RangeError)
|
62
56
|
end
|
63
57
|
end
|
64
58
|
|
65
59
|
describe ".create_from_type" do
|
66
60
|
it "creates with valid types", :aggregate_failures do
|
67
|
-
type = described_class.create_from_type(json_type,
|
68
|
-
new_type = described_class.create_from_type(type,
|
61
|
+
type = described_class.create_from_type(json_type, "date", :date)
|
62
|
+
new_type = described_class.create_from_type(type, "val", :integer)
|
69
63
|
|
70
64
|
date = ::Date.new(2016, 6, 22)
|
71
65
|
|
72
|
-
expect(type.
|
73
|
-
expect(new_type.
|
66
|
+
expect(type.cast(date: "2016-06-22", val: "1.2")).to eq("date" => date, "val" => "1.2")
|
67
|
+
expect(new_type.cast(date: "2016-06-22", val: "1.2")).to eq("date" => date, "val" => 1)
|
74
68
|
end
|
75
69
|
end
|
76
70
|
end
|
77
71
|
|
78
72
|
context "with yaml coder" do
|
73
|
+
if RAILS_5_1
|
74
|
+
let(:yaml_type) do
|
75
|
+
ActiveRecord::Type::Serialized.new(
|
76
|
+
ActiveRecord::Type::Text.new,
|
77
|
+
ActiveRecord::Store::IndifferentCoder.new(
|
78
|
+
"test",
|
79
|
+
ActiveRecord::Coders::YAMLColumn.new("test", Hash)
|
80
|
+
)
|
81
|
+
)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
let(:yaml_type) do
|
85
|
+
ActiveRecord::Type::Serialized.new(
|
86
|
+
ActiveRecord::Type::Text.new,
|
87
|
+
ActiveRecord::Store::IndifferentCoder.new(
|
88
|
+
ActiveRecord::Coders::YAMLColumn.new(Hash)
|
89
|
+
)
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
79
94
|
let(:subject) { described_class.new(yaml_type) }
|
80
95
|
|
81
96
|
it "works", :aggregate_failures do
|
82
|
-
subject.add_typed_key(
|
97
|
+
subject.add_typed_key("date", :date)
|
83
98
|
|
84
99
|
date = ::Date.new(2016, 6, 22)
|
85
100
|
|
86
|
-
expect(subject.
|
87
|
-
expect(subject.
|
88
|
-
expect(subject.
|
89
|
-
expect(subject.
|
90
|
-
expect(subject.
|
91
|
-
expect(subject.
|
101
|
+
expect(subject.cast(date: "2016-06-22")).to eq("date" => date)
|
102
|
+
expect(subject.cast("date" => "2016-06-22")).to eq("date" => date)
|
103
|
+
expect(subject.deserialize("---\n:date: 2016-06-22\n")).to eq("date" => date)
|
104
|
+
expect(subject.deserialize("---\ndate: 2016-06-22\n")).to eq("date" => date)
|
105
|
+
expect(subject.serialize(date: date)).to eq "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\ndate: 2016-06-22\n"
|
106
|
+
expect(subject.serialize("date" => date)).to eq "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\ndate: 2016-06-22\n"
|
92
107
|
end
|
93
108
|
end
|
94
109
|
end
|
data/spec/support/money_type.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class MoneyType < ActiveRecord::Type::Integer
|
2
|
-
def
|
3
|
-
if !value.is_a?(Numeric) && value.include?(
|
4
|
-
price_in_dollars = value.delete(
|
4
|
+
def cast(value)
|
5
|
+
if !value.is_a?(Numeric) && value.include?("$")
|
6
|
+
price_in_dollars = value.delete("$").to_f
|
5
7
|
super(price_in_dollars * 100)
|
6
8
|
else
|
7
9
|
super
|
@@ -9,4 +11,4 @@ class MoneyType < ActiveRecord::Type::Integer
|
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
|
-
ActiveRecord::
|
14
|
+
ActiveRecord::Type.register(:money_type, MoneyType)
|
data/spec/support/user.rb
CHANGED
data/store_attribute.gemspec
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
$:.push File.expand_path("../lib", __FILE__)
|
2
4
|
|
3
5
|
# Maintain your gem's version:
|
@@ -17,11 +19,16 @@ Gem::Specification.new do |s|
|
|
17
19
|
s.files = `git ls-files`.split($/)
|
18
20
|
s.require_paths = ["lib"]
|
19
21
|
|
20
|
-
s.
|
22
|
+
s.required_ruby_version = ">= 2.4.0"
|
23
|
+
|
24
|
+
s.add_runtime_dependency "activerecord", ">= 5.0"
|
21
25
|
|
22
|
-
s.add_development_dependency "pg", "
|
23
|
-
s.add_development_dependency "rake", "
|
26
|
+
s.add_development_dependency "pg", ">= 0.18"
|
27
|
+
s.add_development_dependency "rake", ">= 10.1"
|
24
28
|
s.add_development_dependency "simplecov", ">= 0.3.8"
|
25
29
|
s.add_development_dependency "pry-byebug"
|
26
|
-
s.add_development_dependency "
|
30
|
+
s.add_development_dependency "rubocop", "~> 0.65.0"
|
31
|
+
s.add_development_dependency "rubocop-md", "~> 0.2"
|
32
|
+
s.add_development_dependency "standard", "~> 0.0.36"
|
33
|
+
s.add_development_dependency "rspec", ">= 3.5.0"
|
27
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: store_attribute
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- palkan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,40 +16,40 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '5.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '5.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0.18'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.18'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '10.1'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '10.1'
|
55
55
|
- !ruby/object:Gem::Dependency
|
@@ -81,19 +81,61 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.65.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.65.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-md
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.2'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.2'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: standard
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
86
114
|
requirements:
|
87
115
|
- - "~>"
|
88
116
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
117
|
+
version: 0.0.36
|
90
118
|
type: :development
|
91
119
|
prerelease: false
|
92
120
|
version_requirements: !ruby/object:Gem::Requirement
|
93
121
|
requirements:
|
94
122
|
- - "~>"
|
95
123
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
124
|
+
version: 0.0.36
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 3.5.0
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 3.5.0
|
97
139
|
description: ActiveRecord extension which adds typecasting to store accessors
|
98
140
|
email:
|
99
141
|
- dementiev.vm@gmail.com
|
@@ -113,8 +155,9 @@ files:
|
|
113
155
|
- bench/setup.rb
|
114
156
|
- bin/console
|
115
157
|
- bin/setup
|
116
|
-
- gemfiles/
|
117
|
-
- gemfiles/
|
158
|
+
- gemfiles/rails5.gemfile
|
159
|
+
- gemfiles/rails6.gemfile
|
160
|
+
- gemfiles/railsmaster.gemfile
|
118
161
|
- lib/store_attribute.rb
|
119
162
|
- lib/store_attribute/active_record.rb
|
120
163
|
- lib/store_attribute/active_record/store.rb
|
@@ -138,15 +181,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
181
|
requirements:
|
139
182
|
- - ">="
|
140
183
|
- !ruby/object:Gem::Version
|
141
|
-
version:
|
184
|
+
version: 2.4.0
|
142
185
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
186
|
requirements:
|
144
187
|
- - ">="
|
145
188
|
- !ruby/object:Gem::Version
|
146
189
|
version: '0'
|
147
190
|
requirements: []
|
148
|
-
|
149
|
-
rubygems_version: 2.6.4
|
191
|
+
rubygems_version: 3.0.2
|
150
192
|
signing_key:
|
151
193
|
specification_version: 4
|
152
194
|
summary: ActiveRecord extension which adds typecasting to store accessors
|