hstore_accessor 0.2.0 → 0.3.0

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: b556f080454175cefffda39846659bbc7eb676ea
4
- data.tar.gz: c3b7cecfc960a65db15944e4f8579d941f59a708
3
+ metadata.gz: ed6227f16f31ca2c856584b8e72ae0fb9065f659
4
+ data.tar.gz: f08a8c2cdf12a47e7ed620289fdbf9fa1677e46b
5
5
  SHA512:
6
- metadata.gz: a96e5670fc7ff405146da7f0560bb67af29738ffbae2c209db60d6231cdea90624f8b61c2d8936d88d3c8688a7229ebbd07f6169b9e9816a8583e2a44c066a72
7
- data.tar.gz: fde2a6addc657fe910ff609c087e90673855a88de590256481c36c3a69a797ae2dbc563b8dbf0afa2af259efb2fe53689e4336fedb86d5ed44240e911d2e3299
6
+ metadata.gz: 9617209ac08dd3fbc9a48fdd56866396bb3098e127f66e51c9182f09f565f21f2e9cd084917c9e31f3b60c0e99aec11b312f70d1213df4e80f995d41479267db
7
+ data.tar.gz: fd258525d2f42d3fc42604d000b1302938459d5ad3d65c5d994c11a3209932b0c9181a4058c4144bcbb54c14fcdd3d45aeadd9431655ad3d6a4cf94b4f020819
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ logfile
data/README.md CHANGED
@@ -27,7 +27,7 @@ Or install it yourself as:
27
27
  The `hstore_accessor` method accepts the name of the hstore column you'd
28
28
  like to use and a hash with keys representing fields and values
29
29
  indicating the type to be stored in that field. The available types
30
- are: `string`, `integer`, `float`, `time`, `array`, and `hash`.
30
+ are: `string`, `integer`, `float`, `time`, `boolean`, `array`, and `hash`.
31
31
 
32
32
  ```ruby
33
33
  class Product < ActiveRecord::Base
@@ -51,6 +51,7 @@ p.color = "green"
51
51
  p.weight = 34
52
52
  p.price = 99.95
53
53
  p.built_at = Time.now - 10.days
54
+ p.popular = true
54
55
  p.tags = ["housewares", "kitchen"]
55
56
  p.ratings = { user_a: 3, user_b: 4 }
56
57
  ```
@@ -58,14 +59,14 @@ p.ratings = { user_a: 3, user_b: 4 }
58
59
  Reading these fields works as well.
59
60
 
60
61
  ```ruby
61
- p.color #=> "green
62
+ p.color #=> "green"
62
63
  p.tags #=> ["housewares", "kitchen"]
63
64
  ```
64
65
 
65
66
  ### Scopes
66
67
 
67
68
  The `hstore_accessor` macro also creates scopes for `string`, `integer`,
68
- `float`, `time`, and `array` fields.
69
+ `float`, `time`, `boolean`, and `array` fields.
69
70
 
70
71
  #### String Fields
71
72
 
@@ -108,6 +109,15 @@ Product.tags_contains("kitchen") # tags containing a single val
108
109
  Product.tags_contains(["housewares", "kitchen"]) # tags containing a number of values
109
110
  ```
110
111
 
112
+ #### Boolean Fields
113
+
114
+ Two scopes are created for `boolean` fields:
115
+
116
+ ```ruby
117
+ Product.is_popular # => when populer is set to true
118
+ Product.not_popular # => when populer is set to false
119
+ ```
120
+
111
121
  ### Single-table Inheritance
112
122
 
113
123
  One of the big issues with `ActiveRecord` single-table inheritance (STI)
@@ -165,6 +175,7 @@ individual fields in an `hstore` column.
165
175
  This approach was originally concieved by Joe Hirn in [this blog
166
176
  post](http://www.devmynd.com/blog/2013-3-single-table-inheritance-hstore-lovely-combination).
167
177
 
178
+
168
179
  ## Contributing
169
180
 
170
181
  1. Fork it
@@ -6,7 +6,7 @@ module HstoreAccessor
6
6
 
7
7
  InvalidDataTypeError = Class.new(StandardError)
8
8
 
9
- VALID_TYPES = [:string, :integer, :float, :time, :array, :hash]
9
+ VALID_TYPES = [:string, :integer, :float, :time, :boolean, :array, :hash]
10
10
 
11
11
  SEPARATOR = ";|;"
12
12
 
@@ -14,17 +14,19 @@ module HstoreAccessor
14
14
  DEFAULT_DESERIALIZER = ->(value) { value.to_s }
15
15
 
16
16
  SERIALIZERS = {
17
- :array => ->(value) { value.join(SEPARATOR) },
18
- :hash => ->(value) { value.to_json },
19
- :time => ->(value) { value.to_i }
17
+ :array => -> value { (value && value.join(SEPARATOR)) || "" },
18
+ :hash => -> value { (value && value.to_json) || {} },
19
+ :time => -> value { value.to_i },
20
+ :boolean => -> value { (value == true).to_s }
20
21
  }
21
22
 
22
23
  DESERIALIZERS = {
23
- :array => ->(value) { value.split(SEPARATOR) },
24
- :hash => ->(value) { JSON.parse(value) },
25
- :integer => ->(value) { value.to_i },
26
- :float => ->(value) { value.to_f },
27
- :time => ->(value) { Time.at(value.to_i) }
24
+ :array => -> value { (value && value.split(SEPARATOR)) || [] },
25
+ :hash => -> value { JSON.parse(value) },
26
+ :integer => -> value { value.to_i },
27
+ :float => -> value { value.to_f },
28
+ :time => -> value { Time.at(value.to_i) },
29
+ :boolean => -> value { value == "true" }
28
30
  }
29
31
 
30
32
  def self.included(base)
@@ -59,23 +61,28 @@ module HstoreAccessor
59
61
  deserialize(type, value)
60
62
  end
61
63
 
64
+ query_field = "#{hstore_attribute} -> '#{key}'"
65
+
62
66
  case type
63
67
  when :string
64
- send(:scope, "with_#{key}", -> value { where("#{hstore_attribute} -> '#{key}' = ?", value.to_s) })
68
+ send(:scope, "with_#{key}", -> value { where("#{query_field} = ?", value.to_s) })
65
69
  when :integer, :float
66
- send(:scope, "#{key}_lt", -> value { where("#{hstore_attribute} -> '#{key}' < ?", value.to_s) })
67
- send(:scope, "#{key}_lte", -> value { where("#{hstore_attribute} -> '#{key}' <= ?", value.to_s) })
68
- send(:scope, "#{key}_eq", -> value { where("#{hstore_attribute} -> '#{key}' = ?", value.to_s) })
69
- send(:scope, "#{key}_gte", -> value { where("#{hstore_attribute} -> '#{key}' >= ?", value.to_s) })
70
- send(:scope, "#{key}_gt", -> value { where("#{hstore_attribute} -> '#{key}' > ?", value.to_s) })
70
+ send(:scope, "#{key}_lt", -> value { where("(#{query_field})::#{type} < ?", value.to_s) })
71
+ send(:scope, "#{key}_lte", -> value { where("(#{query_field})::#{type} <= ?", value.to_s) })
72
+ send(:scope, "#{key}_eq", -> value { where("(#{query_field})::#{type} = ?", value.to_s) })
73
+ send(:scope, "#{key}_gte", -> value { where("(#{query_field})::#{type} >= ?", value.to_s) })
74
+ send(:scope, "#{key}_gt", -> value { where("(#{query_field})::#{type} > ?", value.to_s) })
71
75
  when :time
72
- send(:scope, "#{key}_before", -> value { where("to_number(#{hstore_attribute} -> '#{key}', '99999999999') < ?", value.to_i) })
73
- send(:scope, "#{key}_eq", -> value { where("to_number(#{hstore_attribute} -> '#{key}', '99999999999') = ?", value.to_i) })
74
- send(:scope, "#{key}_after", -> value { where("to_number(#{hstore_attribute} -> '#{key}', '99999999999') > ?", value.to_i) })
76
+ send(:scope, "#{key}_before", -> value { where("(#{query_field})::integer < ?", value.to_i) })
77
+ send(:scope, "#{key}_eq", -> value { where("(#{query_field})::integer = ?", value.to_i) })
78
+ send(:scope, "#{key}_after", -> value { where("(#{query_field})::integer > ?", value.to_i) })
79
+ when :boolean
80
+ send(:scope, "is_#{key}", -> { where("#{query_field} = 'true'") })
81
+ send(:scope, "not_#{key}", -> { where("#{query_field} = 'false'") })
75
82
  when :array
76
- send(:scope, "#{key}_eq", -> value { where("#{hstore_attribute} -> '#{key}' = ?", value.join(SEPARATOR)) })
83
+ send(:scope, "#{key}_eq", -> value { where("#{query_field} = ?", value.join(SEPARATOR)) })
77
84
  send(:scope, "#{key}_contains", -> value do
78
- where("string_to_array(#{hstore_attribute} -> '#{key}', '#{SEPARATOR}') @> string_to_array(?, '#{SEPARATOR}')", Array[value].flatten)
85
+ where("string_to_array(#{query_field}, '#{SEPARATOR}') @> string_to_array(?, '#{SEPARATOR}')", Array[value].flatten)
79
86
  end)
80
87
  end
81
88
  end
@@ -1,3 +1,3 @@
1
1
  module HstoreAccessor
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -6,9 +6,10 @@ class Product < ActiveRecord::Base
6
6
  color: :string,
7
7
  price: :integer,
8
8
  weight: :float,
9
+ popular: :boolean,
10
+ build_timestamp: :time,
9
11
  tags: :array,
10
- reviews: :hash,
11
- build_timestamp: :time
12
+ reviews: :hash
12
13
  end
13
14
 
14
15
  describe HstoreAccessor do
@@ -43,9 +44,9 @@ describe HstoreAccessor do
43
44
  describe "scopes" do
44
45
 
45
46
  let!(:timestamp) { Time.now }
46
- let!(:product_a) { Product.create(color: "green", price: 10, weight: 10.1, tags: ["tag1", "tag2", "tag3"], build_timestamp: (timestamp - 10.days)) }
47
- let!(:product_b) { Product.create(color: "orange", price: 20, weight: 20.2, tags: ["tag2", "tag3", "tag4"], build_timestamp: (timestamp - 5.days)) }
48
- let!(:product_c) { Product.create(color: "blue", price: 30, weight: 30.3, tags: ["tag3", "tag4", "tag5"], build_timestamp: timestamp) }
47
+ let!(:product_a) { Product.create(color: "green", price: 10, weight: 10.1, tags: ["tag1", "tag2", "tag3"], popular: true, build_timestamp: (timestamp - 10.days)) }
48
+ let!(:product_b) { Product.create(color: "orange", price: 20, weight: 20.2, tags: ["tag2", "tag3", "tag4"], popular: false, build_timestamp: (timestamp - 5.days)) }
49
+ let!(:product_c) { Product.create(color: "blue", price: 30, weight: 30.3, tags: ["tag3", "tag4", "tag5"], popular: true, build_timestamp: timestamp) }
49
50
 
50
51
  context "for string fields support" do
51
52
 
@@ -132,6 +133,18 @@ describe HstoreAccessor do
132
133
 
133
134
  end
134
135
 
136
+ context "for boolean field support" do
137
+
138
+ it "true" do
139
+ expect(Product.is_popular).to eq [product_a, product_c]
140
+ end
141
+
142
+ it "false" do
143
+ expect(Product.not_popular).to eq [product_b]
144
+ end
145
+
146
+ end
147
+
135
148
  end
136
149
 
137
150
  context "when assigning values it" do
@@ -166,6 +179,13 @@ describe HstoreAccessor do
166
179
  expect(product.tags).to eq ["household", "living room", "kitchen"]
167
180
  end
168
181
 
182
+ it "returns an empty array if an array value is not set" do
183
+ product.tags = nil
184
+ product.save
185
+ product.reload
186
+ expect(product.tags).to eq []
187
+ end
188
+
169
189
  it "correctly stores hash values" do
170
190
  product.reviews = { "user_123" => "4 stars", "user_994" => "3 stars" }
171
191
  product.save
@@ -173,6 +193,13 @@ describe HstoreAccessor do
173
193
  expect(product.reviews).to eq({ "user_123" => "4 stars", "user_994" => "3 stars" })
174
194
  end
175
195
 
196
+ it "returns an empty hash if a hash value is not set" do
197
+ product.reviews = nil
198
+ product.save
199
+ product.reload
200
+ expect(product.reviews).to eq({})
201
+ end
202
+
176
203
  it "correctly stores time values" do
177
204
  timestamp = Time.now - 10.days
178
205
  product.build_timestamp = timestamp
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.2.0
4
+ version: 0.3.0
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: 2013-06-11 00:00:00.000000000 Z
13
+ date: 2013-06-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: pg