hstore_accessor 0.5.4 → 0.5.5
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/lib/hstore_accessor.rb +5 -127
- data/lib/hstore_accessor/macro.rb +76 -0
- data/lib/hstore_accessor/serialization.rb +45 -0
- data/lib/hstore_accessor/time_helper.rb +1 -1
- data/lib/hstore_accessor/type_helpers.rb +20 -0
- data/lib/hstore_accessor/version.rb +1 -1
- data/spec/hstore_accessor_spec.rb +20 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e53358ab1b69b9ae41faf29e52c620348a9241e
|
4
|
+
data.tar.gz: dbccbde5437d1aa332eaa60f0d8d500bd822b720
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69c6b255e86c6f15e96011c3641c0719bc7629927c96d9438368b7daaaf0ea6713393f4b17e69fa3d12bd12dac7a66738c5bb1b7c5027b822d5d1bafbe5274ca
|
7
|
+
data.tar.gz: dd2450efa79dcac2f941348471d8775207b0f6682a7f531025c6c08d505170d21cea39cfb4e7e4dca71db311244292fe7bb9048e94e556661e26553d3bcf228e
|
data/lib/hstore_accessor.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
require "hstore_accessor/version"
|
2
|
+
require "hstore_accessor/type_helpers"
|
3
|
+
require "hstore_accessor/serialization"
|
4
|
+
require "hstore_accessor/macro"
|
2
5
|
require "hstore_accessor/time_helper"
|
3
6
|
require "active_support"
|
4
7
|
require "active_record"
|
@@ -6,133 +9,8 @@ require "bigdecimal"
|
|
6
9
|
|
7
10
|
module HstoreAccessor
|
8
11
|
extend ActiveSupport::Concern
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
VALID_TYPES = [:string, :integer, :float, :time, :boolean, :array, :hash, :date, :decimal]
|
13
|
-
|
14
|
-
SEPARATOR = "||;||"
|
15
|
-
|
16
|
-
DEFAULT_SERIALIZER = ->(value) { value.to_s }
|
17
|
-
DEFAULT_DESERIALIZER = DEFAULT_SERIALIZER
|
18
|
-
|
19
|
-
SERIALIZERS = {
|
20
|
-
array: -> value { (value && value.join(SEPARATOR)) || nil },
|
21
|
-
hash: -> value { (value && value.to_json) || nil },
|
22
|
-
time: -> value { value.to_i },
|
23
|
-
boolean: -> value { (value.to_s == "true").to_s },
|
24
|
-
date: -> value { (value && value.to_s) || nil }
|
25
|
-
}
|
26
|
-
|
27
|
-
DESERIALIZERS = {
|
28
|
-
array: -> value { (value && value.split(SEPARATOR)) || nil },
|
29
|
-
hash: -> value { (value && JSON.parse(value)) || nil },
|
30
|
-
integer: -> value { value.to_i },
|
31
|
-
float: -> value { value.to_f },
|
32
|
-
time: -> value { Time.at(value.to_i) },
|
33
|
-
boolean: -> value { value == "true" },
|
34
|
-
date: -> value { (value && Date.parse(value)) || nil },
|
35
|
-
decimal: -> value { BigDecimal.new(value) }
|
36
|
-
}
|
37
|
-
|
38
|
-
def serialize(type, value, serializer=nil)
|
39
|
-
return nil if value.nil?
|
40
|
-
serializer ||= (SERIALIZERS[type] || DEFAULT_SERIALIZER)
|
41
|
-
serializer.call(value)
|
42
|
-
end
|
43
|
-
|
44
|
-
def deserialize(type, value, deserializer=nil)
|
45
|
-
return nil if value.nil?
|
46
|
-
deserializer ||= (DESERIALIZERS[type] || DEFAULT_DESERIALIZER)
|
47
|
-
deserializer.call(value)
|
48
|
-
end
|
49
|
-
|
50
|
-
def type_cast(type, value)
|
51
|
-
return nil if value.nil?
|
52
|
-
column_class = ActiveRecord::ConnectionAdapters::Column
|
53
|
-
case type
|
54
|
-
when :string,:hash,:array,
|
55
|
-
:decimal then value
|
56
|
-
when :integer then column_class.value_to_integer(value)
|
57
|
-
when :float then value.to_f
|
58
|
-
when :time then TimeHelper.string_to_time(value)
|
59
|
-
when :date then column_class.value_to_date(value)
|
60
|
-
when :boolean then column_class.value_to_boolean(value)
|
61
|
-
else value
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
module ClassMethods
|
66
|
-
|
67
|
-
def hstore_accessor(hstore_attribute, fields)
|
68
|
-
fields.each do |key, type|
|
69
|
-
|
70
|
-
data_type = type
|
71
|
-
store_key = key
|
72
|
-
if type.is_a?(Hash)
|
73
|
-
type = type.with_indifferent_access
|
74
|
-
data_type = type[:data_type]
|
75
|
-
store_key = type[:store_key]
|
76
|
-
end
|
77
|
-
|
78
|
-
data_type = data_type.to_sym
|
79
|
-
|
80
|
-
raise InvalidDataTypeError unless VALID_TYPES.include?(data_type)
|
81
|
-
|
82
|
-
define_method("hstore_metadata_for_#{hstore_attribute}") do
|
83
|
-
fields
|
84
|
-
end
|
85
|
-
|
86
|
-
define_method("#{key}=") do |value|
|
87
|
-
send("#{hstore_attribute}=", (send(hstore_attribute) || {}).merge(store_key.to_s => serialize(data_type, type_cast(type, value))))
|
88
|
-
send("#{hstore_attribute}_will_change!")
|
89
|
-
end
|
90
|
-
|
91
|
-
define_method(key) do
|
92
|
-
value = send(hstore_attribute) && send(hstore_attribute).with_indifferent_access[store_key.to_s]
|
93
|
-
deserialize(data_type, value)
|
94
|
-
end
|
95
|
-
|
96
|
-
if type == :boolean
|
97
|
-
define_method("#{key}?") do
|
98
|
-
return send("#{key}")
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
query_field = "#{hstore_attribute} -> '#{store_key}'"
|
103
|
-
|
104
|
-
case data_type
|
105
|
-
when :string
|
106
|
-
send(:scope, "with_#{key}", -> value { where("#{query_field} = ?", value.to_s) })
|
107
|
-
when :integer, :float, :decimal
|
108
|
-
send(:scope, "#{key}_lt", -> value { where("(#{query_field})::#{data_type} < ?", value.to_s) })
|
109
|
-
send(:scope, "#{key}_lte", -> value { where("(#{query_field})::#{data_type} <= ?", value.to_s) })
|
110
|
-
send(:scope, "#{key}_eq", -> value { where("(#{query_field})::#{data_type} = ?", value.to_s) })
|
111
|
-
send(:scope, "#{key}_gte", -> value { where("(#{query_field})::#{data_type} >= ?", value.to_s) })
|
112
|
-
send(:scope, "#{key}_gt", -> value { where("(#{query_field})::#{data_type} > ?", value.to_s) })
|
113
|
-
when :time
|
114
|
-
send(:scope, "#{key}_before", -> value { where("(#{query_field})::integer < ?", value.to_i) })
|
115
|
-
send(:scope, "#{key}_eq", -> value { where("(#{query_field})::integer = ?", value.to_i) })
|
116
|
-
send(:scope, "#{key}_after", -> value { where("(#{query_field})::integer > ?", value.to_i) })
|
117
|
-
when :date
|
118
|
-
send(:scope, "#{key}_before", -> value { where("#{query_field} < ?", value.to_s) })
|
119
|
-
send(:scope, "#{key}_eq", -> value { where("#{query_field} = ?", value.to_s) })
|
120
|
-
send(:scope, "#{key}_after", -> value { where("#{query_field} > ?", value.to_s) })
|
121
|
-
when :boolean
|
122
|
-
send(:scope, "is_#{key}", -> { where("#{query_field} = 'true'") })
|
123
|
-
send(:scope, "not_#{key}", -> { where("#{query_field} = 'false'") })
|
124
|
-
when :array
|
125
|
-
send(:scope, "#{key}_eq", -> value { where("#{query_field} = ?", value.join(SEPARATOR)) })
|
126
|
-
send(:scope, "#{key}_contains", -> value do
|
127
|
-
where("string_to_array(#{query_field}, '#{SEPARATOR}') @> string_to_array(?, '#{SEPARATOR}')", Array[value].flatten)
|
128
|
-
end)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
end
|
133
|
-
|
134
|
-
end
|
135
|
-
|
12
|
+
include Serialization
|
13
|
+
include Macro
|
136
14
|
end
|
137
15
|
|
138
16
|
ActiveSupport.on_load(:active_record) do
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module HstoreAccessor
|
2
|
+
module Macro
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
def hstore_accessor(hstore_attribute, fields)
|
7
|
+
fields.each do |key, type|
|
8
|
+
|
9
|
+
data_type = type
|
10
|
+
store_key = key
|
11
|
+
if type.is_a?(Hash)
|
12
|
+
type = type.with_indifferent_access
|
13
|
+
data_type = type[:data_type]
|
14
|
+
store_key = type[:store_key]
|
15
|
+
end
|
16
|
+
|
17
|
+
data_type = data_type.to_sym
|
18
|
+
|
19
|
+
raise Serialization::InvalidDataTypeError unless Serialization::VALID_TYPES.include?(data_type)
|
20
|
+
|
21
|
+
define_method("hstore_metadata_for_#{hstore_attribute}") do
|
22
|
+
fields
|
23
|
+
end
|
24
|
+
|
25
|
+
define_method("#{key}=") do |value|
|
26
|
+
send("#{hstore_attribute}=", (send(hstore_attribute) || {}).merge(store_key.to_s => serialize(data_type, TypeHelpers.cast(type, value))))
|
27
|
+
send("#{hstore_attribute}_will_change!")
|
28
|
+
end
|
29
|
+
|
30
|
+
define_method(key) do
|
31
|
+
value = send(hstore_attribute) && send(hstore_attribute).with_indifferent_access[store_key.to_s]
|
32
|
+
deserialize(data_type, value)
|
33
|
+
end
|
34
|
+
|
35
|
+
if type == :boolean
|
36
|
+
define_method("#{key}?") do
|
37
|
+
return send("#{key}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
query_field = "#{hstore_attribute} -> '#{store_key}'"
|
42
|
+
|
43
|
+
case data_type
|
44
|
+
when :string
|
45
|
+
send(:scope, "with_#{key}", -> value { where("#{query_field} = ?", value.to_s) })
|
46
|
+
when :integer, :float, :decimal
|
47
|
+
send(:scope, "#{key}_lt", -> value { where("(#{query_field})::#{data_type} < ?", value.to_s) })
|
48
|
+
send(:scope, "#{key}_lte", -> value { where("(#{query_field})::#{data_type} <= ?", value.to_s) })
|
49
|
+
send(:scope, "#{key}_eq", -> value { where("(#{query_field})::#{data_type} = ?", value.to_s) })
|
50
|
+
send(:scope, "#{key}_gte", -> value { where("(#{query_field})::#{data_type} >= ?", value.to_s) })
|
51
|
+
send(:scope, "#{key}_gt", -> value { where("(#{query_field})::#{data_type} > ?", value.to_s) })
|
52
|
+
when :time
|
53
|
+
send(:scope, "#{key}_before", -> value { where("(#{query_field})::integer < ?", value.to_i) })
|
54
|
+
send(:scope, "#{key}_eq", -> value { where("(#{query_field})::integer = ?", value.to_i) })
|
55
|
+
send(:scope, "#{key}_after", -> value { where("(#{query_field})::integer > ?", value.to_i) })
|
56
|
+
when :date
|
57
|
+
send(:scope, "#{key}_before", -> value { where("#{query_field} < ?", value.to_s) })
|
58
|
+
send(:scope, "#{key}_eq", -> value { where("#{query_field} = ?", value.to_s) })
|
59
|
+
send(:scope, "#{key}_after", -> value { where("#{query_field} > ?", value.to_s) })
|
60
|
+
when :boolean
|
61
|
+
send(:scope, "is_#{key}", -> { where("#{query_field} = 'true'") })
|
62
|
+
send(:scope, "not_#{key}", -> { where("#{query_field} = 'false'") })
|
63
|
+
when :array
|
64
|
+
send(:scope, "#{key}_eq", -> value { where("#{query_field} = ?", value.join(Serialization::SEPARATOR)) })
|
65
|
+
send(:scope, "#{key}_contains", -> value do
|
66
|
+
where("string_to_array(#{query_field}, '#{Serialization::SEPARATOR}') @> string_to_array(?, '#{Serialization::SEPARATOR}')", Array[value].flatten)
|
67
|
+
end)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module HstoreAccessor
|
2
|
+
module Serialization
|
3
|
+
|
4
|
+
InvalidDataTypeError = Class.new(StandardError)
|
5
|
+
|
6
|
+
VALID_TYPES = [:string, :integer, :float, :time, :boolean, :array, :hash, :date, :decimal]
|
7
|
+
|
8
|
+
SEPARATOR = "||;||"
|
9
|
+
|
10
|
+
DEFAULT_SERIALIZER = ->(value) { value.to_s }
|
11
|
+
DEFAULT_DESERIALIZER = DEFAULT_SERIALIZER
|
12
|
+
|
13
|
+
SERIALIZERS = {
|
14
|
+
array: -> value { (value && value.join(SEPARATOR)) || nil },
|
15
|
+
hash: -> value { (value && value.to_json) || nil },
|
16
|
+
time: -> value { value.to_i },
|
17
|
+
boolean: -> value { (value.to_s == "true").to_s },
|
18
|
+
date: -> value { (value && value.to_s) || nil }
|
19
|
+
}
|
20
|
+
|
21
|
+
DESERIALIZERS = {
|
22
|
+
array: -> value { (value && value.split(SEPARATOR)) || nil },
|
23
|
+
hash: -> value { (value && JSON.parse(value)) || nil },
|
24
|
+
integer: -> value { value.to_i },
|
25
|
+
float: -> value { value.to_f },
|
26
|
+
time: -> value { Time.at(value.to_i) },
|
27
|
+
boolean: -> value { TypeHelpers.cast(:boolean, value) },
|
28
|
+
date: -> value { (value && Date.parse(value)) || nil },
|
29
|
+
decimal: -> value { BigDecimal.new(value) }
|
30
|
+
}
|
31
|
+
|
32
|
+
def serialize(type, value, serializer=nil)
|
33
|
+
return nil if value.nil?
|
34
|
+
serializer ||= (SERIALIZERS[type] || DEFAULT_SERIALIZER)
|
35
|
+
serializer.call(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def deserialize(type, value, deserializer=nil)
|
39
|
+
return nil if value.nil?
|
40
|
+
deserializer ||= (DESERIALIZERS[type] || DEFAULT_DESERIALIZER)
|
41
|
+
deserializer.call(value)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module HstoreAccessor
|
2
2
|
module TimeHelper
|
3
3
|
|
4
|
-
# There is a bug in ActiveRecord::ConnectionAdapters::Column#string_to_time
|
4
|
+
# There is a bug in ActiveRecord::ConnectionAdapters::Column#string_to_time
|
5
5
|
# which drops the timezone. This has been fixed, but not released.
|
6
6
|
# This method includes the fix. See: https://github.com/rails/rails/pull/12290
|
7
7
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module HstoreAccessor
|
2
|
+
module TypeHelpers
|
3
|
+
|
4
|
+
def self.cast(type, value)
|
5
|
+
return nil if value.nil?
|
6
|
+
column_class = ActiveRecord::ConnectionAdapters::Column
|
7
|
+
case type
|
8
|
+
when :string,:hash,:array,
|
9
|
+
:decimal then value
|
10
|
+
when :integer then column_class.value_to_integer(value)
|
11
|
+
when :float then value.to_f
|
12
|
+
when :time then TimeHelper.string_to_time(value)
|
13
|
+
when :date then column_class.value_to_date(value)
|
14
|
+
when :boolean then column_class.value_to_boolean(value)
|
15
|
+
else value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -104,6 +104,26 @@ describe HstoreAccessor do
|
|
104
104
|
expect(product.popular?).to be_false
|
105
105
|
end
|
106
106
|
|
107
|
+
it "return true for boolean field set via hash using real boolean" do
|
108
|
+
product.options = { "popular" => true }
|
109
|
+
expect(product.popular?).to be_true
|
110
|
+
end
|
111
|
+
|
112
|
+
it "return false for boolean field set via hash using real boolean" do
|
113
|
+
product.options = { "popular" => false }
|
114
|
+
expect(product.popular?).to be_false
|
115
|
+
end
|
116
|
+
|
117
|
+
it "return true for boolean field set via hash using string" do
|
118
|
+
product.options = { "popular" => "true" }
|
119
|
+
expect(product.popular?).to be_true
|
120
|
+
end
|
121
|
+
|
122
|
+
it "return false for boolean field set via hash using string" do
|
123
|
+
product.options = { "popular" => "false" }
|
124
|
+
expect(product.popular?).to be_false
|
125
|
+
end
|
126
|
+
|
107
127
|
end
|
108
128
|
|
109
129
|
describe "scopes" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hstore_accessor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Hirn
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2014-03-
|
13
|
+
date: 2014-03-22 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -126,7 +126,10 @@ files:
|
|
126
126
|
- Rakefile
|
127
127
|
- hstore_accessor.gemspec
|
128
128
|
- lib/hstore_accessor.rb
|
129
|
+
- lib/hstore_accessor/macro.rb
|
130
|
+
- lib/hstore_accessor/serialization.rb
|
129
131
|
- lib/hstore_accessor/time_helper.rb
|
132
|
+
- lib/hstore_accessor/type_helpers.rb
|
130
133
|
- lib/hstore_accessor/version.rb
|
131
134
|
- spec/hstore_accessor_spec.rb
|
132
135
|
- spec/spec_helper.rb
|