hstore_accessor 0.5.4 → 0.5.5

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: 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