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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7288a6f9802b78a2c213dfd478de0d5963bd0947
4
- data.tar.gz: 1b73b39baa507fbd1bdfc9380ef825832466ab51
3
+ metadata.gz: 2e53358ab1b69b9ae41faf29e52c620348a9241e
4
+ data.tar.gz: dbccbde5437d1aa332eaa60f0d8d500bd822b720
5
5
  SHA512:
6
- metadata.gz: 3c608a5b3e069c9e325257183ea5056c15060ca8e590a6f85626606feaa506a1f2f46b42243d8c2bf556d851c81cec202abd864f587cd9d855dd75405e1cfb00
7
- data.tar.gz: cb83f6c2a8b239fe43fdf267c41e57ca6893ea80cae4d3a2fd8f4843005dc394cdb70f23ac67587dac2c91d30dd754e5411d35bc01275b93fa9ad28105bfa380
6
+ metadata.gz: 69c6b255e86c6f15e96011c3641c0719bc7629927c96d9438368b7daaaf0ea6713393f4b17e69fa3d12bd12dac7a66738c5bb1b7c5027b822d5d1bafbe5274ca
7
+ data.tar.gz: dd2450efa79dcac2f941348471d8775207b0f6682a7f531025c6c08d505170d21cea39cfb4e7e4dca71db311244292fe7bb9048e94e556661e26553d3bcf228e
@@ -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
- InvalidDataTypeError = Class.new(StandardError)
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
@@ -1,3 +1,3 @@
1
1
  module HstoreAccessor
2
- VERSION = "0.5.4"
2
+ VERSION = "0.5.5"
3
3
  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
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-02 00:00:00.000000000 Z
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