nested-hstore 0.0.1
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.
- data/MIT-LICENSE +20 -0
- data/README.md +68 -0
- data/Rakefile +12 -0
- data/lib/active_record/coders/nested_hstore.rb +20 -0
- data/lib/nested-hstore.rb +8 -0
- data/lib/nested_hstore/serializer.rb +117 -0
- data/lib/nested_hstore/version.rb +3 -0
- metadata +101 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 Tom Benner
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
Nested Hstore
|
2
|
+
=============
|
3
|
+
Store nested hashes and other types in ActiveRecord hstores
|
4
|
+
|
5
|
+
Overview
|
6
|
+
--------
|
7
|
+
|
8
|
+
Postgres hstores offer a number of benefits, but they don't natively support multi-level data. Nested Hstore adds this support to ActiveRecord, letting you treat an hstore like a NoSQL-like document.
|
9
|
+
|
10
|
+
[Hstore functions](http://www.postgresql.org/docs/9.1/static/hstore.html) are still supported at the root level.
|
11
|
+
|
12
|
+
It also lets you store data types other than hashes in an hstore. All of the following values will be returned verbatim:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# Nested hash
|
16
|
+
post.my_hstore = {
|
17
|
+
'title' => 'My Post',
|
18
|
+
'comment_ids' => [34, 67, 82],
|
19
|
+
'user' => {
|
20
|
+
'id' => 15,
|
21
|
+
'username' => 'janedoe',
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
# Array
|
26
|
+
post.my_hstore = [34, 67, 82]
|
27
|
+
|
28
|
+
# Array of nested hashes
|
29
|
+
post.my_hstore = [
|
30
|
+
{
|
31
|
+
'id' => 15,
|
32
|
+
'username' => 'janedoe'
|
33
|
+
},
|
34
|
+
{
|
35
|
+
'id' => 16,
|
36
|
+
'username' => 'johndoe'
|
37
|
+
}
|
38
|
+
]
|
39
|
+
|
40
|
+
# Integer
|
41
|
+
post.my_hstore = 43
|
42
|
+
|
43
|
+
# Float
|
44
|
+
post.my_hstore = 43.1
|
45
|
+
|
46
|
+
# String
|
47
|
+
post.my_hstore = 'janedoe'
|
48
|
+
```
|
49
|
+
|
50
|
+
Installation
|
51
|
+
------------
|
52
|
+
|
53
|
+
Include it in your Gemfile:
|
54
|
+
|
55
|
+
gem 'nested-hstore'
|
56
|
+
|
57
|
+
Set up [activerecord-postgres-hstore](https://github.com/diogob/activerecord-postgres-hstore) if you haven't already. Instead of using ActiveRecord::Coders::Hstore, use:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
class Post < ActiveRecord::Base
|
61
|
+
serialize :my_store, ActiveRecord::Coders::NestedHstore
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
License
|
66
|
+
-------
|
67
|
+
|
68
|
+
Nested Hstore is released under the MIT License. Please see the MIT-LICENSE file for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Coders
|
3
|
+
class NestedHstore < Hstore
|
4
|
+
def initialize(default=nil)
|
5
|
+
super(default)
|
6
|
+
@nested_serializer = ::NestedHstore::Serializer.new
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def to_hstore obj
|
12
|
+
super(@nested_serializer.serialize(obj))
|
13
|
+
end
|
14
|
+
|
15
|
+
def from_hstore hstore
|
16
|
+
@nested_serializer.deserialize(super)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module NestedHstore
|
2
|
+
class Serializer
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@type_key = '__TYPE__'
|
6
|
+
@types_map = {
|
7
|
+
array: '__ARRAY__',
|
8
|
+
float: '__FLOAT__',
|
9
|
+
hash: '__HASH__',
|
10
|
+
integer: '__INTEGER__',
|
11
|
+
string: '__STRING__'
|
12
|
+
}
|
13
|
+
@types_map_inverted = @types_map.invert
|
14
|
+
@value_key = '__VALUE__'
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize(value)
|
18
|
+
return nil if value.nil?
|
19
|
+
if value.is_a?(Array)
|
20
|
+
type = :array
|
21
|
+
hash = array_to_hash(value)
|
22
|
+
elsif value.is_a?(Float)
|
23
|
+
type = :float
|
24
|
+
hash = { @value_key => value }
|
25
|
+
elsif value.is_a?(Hash)
|
26
|
+
type = :hash
|
27
|
+
hash = standardize_value(value)
|
28
|
+
elsif value.is_a?(Integer)
|
29
|
+
type = :integer
|
30
|
+
hash = { @value_key => value }
|
31
|
+
elsif value.is_a?(String)
|
32
|
+
type = :string
|
33
|
+
hash = { @value_key => value }
|
34
|
+
else
|
35
|
+
raise "Unsupported hstore type: #{value.class}"
|
36
|
+
end
|
37
|
+
hash_to_hstore(type, hash)
|
38
|
+
end
|
39
|
+
|
40
|
+
def deserialize(hash)
|
41
|
+
return nil if hash.nil?
|
42
|
+
raise 'Hstore value should be a hash' unless hash.is_a?(Hash)
|
43
|
+
type_value = hash.delete(@type_key)
|
44
|
+
type = @types_map_inverted[type_value]
|
45
|
+
deserialized = case type
|
46
|
+
when :array
|
47
|
+
hash.values.map { |v| decode_json_if_json(v) }
|
48
|
+
when :float
|
49
|
+
hash[@value_key].to_f
|
50
|
+
when :hash
|
51
|
+
hash.each do |k, v|
|
52
|
+
hash[k] = decode_json_if_json(v)
|
53
|
+
end
|
54
|
+
hash
|
55
|
+
when :integer
|
56
|
+
hash[@value_key].to_i
|
57
|
+
when :string
|
58
|
+
hash[@value_key]
|
59
|
+
end
|
60
|
+
deserialized
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def hash_to_hstore(type, hash)
|
66
|
+
return {} if type == :hash && hash.blank?
|
67
|
+
|
68
|
+
hstore = hash.dup
|
69
|
+
hstore.each do |k, v|
|
70
|
+
if v.is_a?(Array) || v.is_a?(Hash)
|
71
|
+
hstore[k] = encode_json(v)
|
72
|
+
else
|
73
|
+
hstore[k] = v.to_s
|
74
|
+
end
|
75
|
+
end
|
76
|
+
hstore.merge(@type_key => @types_map[type])
|
77
|
+
end
|
78
|
+
|
79
|
+
def standardize_value(value)
|
80
|
+
if value.is_a?(Array)
|
81
|
+
value.map! do |v|
|
82
|
+
standardize_value(v)
|
83
|
+
end
|
84
|
+
elsif value.is_a?(Hash)
|
85
|
+
value.each do |k, v|
|
86
|
+
value[k] = standardize_value(v)
|
87
|
+
end
|
88
|
+
# Standardize Times to an ISO string, as that's what DateTime#to_s evaluates to
|
89
|
+
elsif value.is_a?(Time) || value.is_a?(ActiveSupport::TimeWithZone)
|
90
|
+
value = value.iso8601
|
91
|
+
end
|
92
|
+
value
|
93
|
+
end
|
94
|
+
|
95
|
+
def array_to_hash(array)
|
96
|
+
hash = {}
|
97
|
+
array.each_with_index do |value, index|
|
98
|
+
hash[index.to_s] = standardize_value(value)
|
99
|
+
end
|
100
|
+
hash
|
101
|
+
end
|
102
|
+
|
103
|
+
# This isn't ideal: how do we know whether each value in an hstore is JSON or a
|
104
|
+
# string/integer/etc?
|
105
|
+
def decode_json_if_json(value)
|
106
|
+
begin
|
107
|
+
ActiveSupport::JSON.decode(value)
|
108
|
+
rescue
|
109
|
+
value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def encode_json(value)
|
114
|
+
ActiveSupport::JSON.encode(value)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nested-hstore
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tom Benner
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-11-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activerecord-postgres-hstore
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: activesupport
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Store nested hashes and other types in ActiveRecord hstores
|
63
|
+
email:
|
64
|
+
- tombenner@gmail.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- lib/active_record/coders/nested_hstore.rb
|
70
|
+
- lib/nested-hstore.rb
|
71
|
+
- lib/nested_hstore/serializer.rb
|
72
|
+
- lib/nested_hstore/version.rb
|
73
|
+
- MIT-LICENSE
|
74
|
+
- Rakefile
|
75
|
+
- README.md
|
76
|
+
homepage: https://github.com/tombenner/nested-hstore
|
77
|
+
licenses:
|
78
|
+
- MIT
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 1.8.24
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: Store nested hashes and other types in ActiveRecord hstores
|
101
|
+
test_files: []
|