hstore_accessor 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|