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 +4 -4
- data/.gitignore +1 -0
- data/README.md +14 -3
- data/lib/hstore_accessor.rb +27 -20
- data/lib/hstore_accessor/version.rb +1 -1
- data/spec/hstore_accessor_spec.rb +32 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed6227f16f31ca2c856584b8e72ae0fb9065f659
|
4
|
+
data.tar.gz: f08a8c2cdf12a47e7ed620289fdbf9fa1677e46b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9617209ac08dd3fbc9a48fdd56866396bb3098e127f66e51c9182f09f565f21f2e9cd084917c9e31f3b60c0e99aec11b312f70d1213df4e80f995d41479267db
|
7
|
+
data.tar.gz: fd258525d2f42d3fc42604d000b1302938459d5ad3d65c5d994c11a3209932b0c9181a4058c4144bcbb54c14fcdd3d45aeadd9431655ad3d6a4cf94b4f020819
|
data/.gitignore
CHANGED
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
|
data/lib/hstore_accessor.rb
CHANGED
@@ -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
|
18
|
-
:hash
|
19
|
-
:time
|
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
|
24
|
-
:hash
|
25
|
-
:integer
|
26
|
-
:float
|
27
|
-
:time
|
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("#{
|
68
|
+
send(:scope, "with_#{key}", -> value { where("#{query_field} = ?", value.to_s) })
|
65
69
|
when :integer, :float
|
66
|
-
send(:scope, "#{key}_lt", -> value { where("#{
|
67
|
-
send(:scope, "#{key}_lte", -> value { where("#{
|
68
|
-
send(:scope, "#{key}_eq", -> value { where("#{
|
69
|
-
send(:scope, "#{key}_gte", -> value { where("#{
|
70
|
-
send(:scope, "#{key}_gt", -> value { where("#{
|
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("
|
73
|
-
send(:scope, "#{key}_eq", -> value { where("
|
74
|
-
send(:scope, "#{key}_after", -> value { where("
|
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("#{
|
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(#{
|
85
|
+
where("string_to_array(#{query_field}, '#{SEPARATOR}') @> string_to_array(?, '#{SEPARATOR}')", Array[value].flatten)
|
79
86
|
end)
|
80
87
|
end
|
81
88
|
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.
|
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-
|
13
|
+
date: 2013-06-12 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: pg
|