store_complex 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +25 -0
- data/.travis.yml +15 -0
- data/CHANGELOG.md +0 -0
- data/CONTRIBUTORS.md +1 -0
- data/Gemfile +4 -0
- data/LICENSE.md +22 -0
- data/README.md +178 -0
- data/Rakefile +5 -0
- data/lib/store_complex/version.rb +3 -0
- data/lib/store_complex.rb +35 -0
- data/store_complex.gemspec +37 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7dd6dd5f9bad33c4202a6da557a89a36e836d97a
|
4
|
+
data.tar.gz: 90fcf9af2d65ac867c0aaa2c6de0fedd15644b6f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5250ee091d440fd8b308949b7d40f10e1c3e4cbb862924c5063d7e91238ce45d209c2b213b9f4f2e875e1ed10ffd396da3b97d5bf80b1a4315c9668475c6739b
|
7
|
+
data.tar.gz: 46ad2e8d03bb125f52883a0fba0ff4dcf66603a5685800415a969138b8fdc860db5c2fe12ed86592ebda1338e6f44304dc6a19443da537040729bd445750557f
|
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
.env
|
7
|
+
.rspec
|
8
|
+
Gemfile.lock
|
9
|
+
InstalledFiles
|
10
|
+
_yardoc
|
11
|
+
coverage
|
12
|
+
doc/
|
13
|
+
lib/bundler/man
|
14
|
+
pkg
|
15
|
+
rdoc
|
16
|
+
spec/reports
|
17
|
+
test/tmp
|
18
|
+
test/version_tmp
|
19
|
+
tmp
|
20
|
+
*.bundle
|
21
|
+
*.so
|
22
|
+
*.o
|
23
|
+
*.a
|
24
|
+
mkmf.log
|
25
|
+
**/.DS_Store
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
File without changes
|
data/CONTRIBUTORS.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* Andrey Pronin <moonfly.msk@gmail.com> aka moonfly (https://github.com/moonfly)
|
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2014 [CONTRIBUTORS.md](https://github.com/moonfly/store_complex/master/CONTRIBUTORS.md)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
# StoreComplex
|
2
|
+
|
3
|
+
Stores complex data that includes Arrays and Hashes (possibly nested) in an attribute inside hstore field. The most typical usage scenario is storing arrays in hstore, but it can handle more complex cases.
|
4
|
+
|
5
|
+
[![Build Status](https://travis-ci.org/moonfly/store_complex.svg?branch=master)](https://travis-ci.org/moonfly/store_complex)
|
6
|
+
[![Coverage Status](https://img.shields.io/coveralls/moonfly/store_complex.svg)](https://coveralls.io/r/moonfly/store_complex?branch=master)
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
gem 'store_complex'
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install store_complex
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Reminder: What Rails already has
|
25
|
+
|
26
|
+
ActiveRecord in Rails 4 already provides methods for dealing with individual attributes inside [PostgresSQL hstore and json](http://www.postgresql.org/docs/9.3/static/hstore.html) fields.
|
27
|
+
|
28
|
+
Let's consider an example. There is a database table (and a corresponding ActiveRecord model) that describes some blog authors.
|
29
|
+
Among other data, it has an hstore field called `properties` that captures various optional bits of information about an author:
|
30
|
+
email, facebook account, personal blog address, etc.
|
31
|
+
|
32
|
+
In Rails 4 you can get convenient access to the individual attributes inside `properties` using [`store_accessor`](http://api.rubyonrails.org/classes/ActiveRecord/Store.html):
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
class Author < ActiveRecord::Base
|
36
|
+
store_accessor :properties, :email, :facebook, :blog
|
37
|
+
end
|
38
|
+
|
39
|
+
author = Author.new(name:'Uber Guru')
|
40
|
+
author.properties # => nil
|
41
|
+
author.email = 'somebody@example.org' # will store this email in properties
|
42
|
+
author.properties # => {"email"=>"somebody@example.org"}
|
43
|
+
|
44
|
+
author.save!
|
45
|
+
|
46
|
+
author = Author.find_by_name('Uber Guru')
|
47
|
+
author.properties # => {"email"=>"somebody@example.org"}
|
48
|
+
author.email # => 'somebody@example.org'
|
49
|
+
```
|
50
|
+
|
51
|
+
But what if we want to let tha authors specify more than a single email. OK, simple, the `email` property will now be an array. Not, so fast...
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
author = Author.new(name:'Uber Guru')
|
55
|
+
author.properties # => nil
|
56
|
+
author.email = ['somebody@example.org','somebody@example.com']
|
57
|
+
author.properties # => {"email"=>["somebody@example.org", "somebody@example.com"]}
|
58
|
+
|
59
|
+
author.save!
|
60
|
+
|
61
|
+
author = Author.find_by_name('Uber Guru')
|
62
|
+
author.properties # => {"email"=>"[\"somebody@example.org\", \"somebody@example.com\"]"}
|
63
|
+
author.email # => "[\"somebody@example.org\", \"somebody@example.com\"]"
|
64
|
+
# Oh-oh! :(
|
65
|
+
```
|
66
|
+
|
67
|
+
And instead of an array we got back a string representation of that array.
|
68
|
+
|
69
|
+
So, can we do something about it? Yes, meet `store_complex`...
|
70
|
+
|
71
|
+
### How to use `store_complex`
|
72
|
+
|
73
|
+
In your model, use `store_complex` in place of `store_accessor`. That's it!
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
class Author < ActiveRecord::Base
|
77
|
+
store_complex :properties, :email
|
78
|
+
end
|
79
|
+
|
80
|
+
author = Author.new(name:'Uber Guru')
|
81
|
+
author.properties # => nil
|
82
|
+
author.email = ['somebody@example.org','somebody@example.com']
|
83
|
+
author.properties # => {"email"=>["somebody@example.org", "somebody@example.com"]}
|
84
|
+
|
85
|
+
author.save!
|
86
|
+
|
87
|
+
author = Author.find_by_name('Uber Guru')
|
88
|
+
author.properties # => {"email"=>"[\"somebody@example.org\", \"somebody@example.com\"]"}
|
89
|
+
author.email # => ["somebody@example.org","somebody@example.com"]
|
90
|
+
# Success! :)
|
91
|
+
```
|
92
|
+
|
93
|
+
|
94
|
+
### What `store_complex` does
|
95
|
+
|
96
|
+
`store_complex` allows to store arrays and hashes in hstore attributes. Those arrays and hashes can contain as their values or keys (for hashes):
|
97
|
+
|
98
|
+
- strings, numbers, booleans, nils - will be stored 'as is';
|
99
|
+
- other arrays and hashes - yes, nesting is possible;
|
100
|
+
- symbols - will be converted to strings;
|
101
|
+
- other data types if they can be converted to strings using `String(value)` - and yes, they **will** be converted to strings.
|
102
|
+
|
103
|
+
One important note: If you store something but array or hash, it will be wrapped into an array. `store_complex` is not for simple data types, use 'store_accessor' for that. The only exception is `nil`, which deletes the attribute from hstore. And if there is no attribute in hstore, the `store_complex` accessor will return an empty array: `[]`.
|
104
|
+
|
105
|
+
Example:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
class Author < ActiveRecord::Base
|
109
|
+
store_complex :properties, :email
|
110
|
+
end
|
111
|
+
|
112
|
+
author = Author.new(name:'Uber Guru')
|
113
|
+
|
114
|
+
author.properties # => nil
|
115
|
+
author.email # => []
|
116
|
+
|
117
|
+
author.email = 'somebody@example.org' #
|
118
|
+
author.email # => ["somebody@example.org"]
|
119
|
+
|
120
|
+
author.email = nil #
|
121
|
+
author.email # => []
|
122
|
+
```
|
123
|
+
|
124
|
+
Another awesome feature of `store_complex` is that it tracks not only assignments to the "complex" attribute, but also all operations on the hash or array (inlcuding those nested within!) that change the object (all those `sort!` and `delete` calls). Thanks to [observable_object gem](https://github.com/moonfly/observable_object) (and me as its author ;-) ) for that awesome behavior.
|
125
|
+
|
126
|
+
To get you excited, here is an example below:
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
class Author < ActiveRecord::Base
|
130
|
+
store_complex :properties, :email
|
131
|
+
end
|
132
|
+
|
133
|
+
author = Author.new(name:'Uber Guru')
|
134
|
+
|
135
|
+
author.properties # => nil
|
136
|
+
author.email # => []
|
137
|
+
|
138
|
+
author.email = {
|
139
|
+
"somebody@example.org" => "personal",
|
140
|
+
"somebody@example.com" => "work",
|
141
|
+
"somebody@business.nowhere" => "work"
|
142
|
+
}
|
143
|
+
|
144
|
+
author.email.delete_if { |k,v| v == 'work' } # delete all work emails
|
145
|
+
|
146
|
+
author.save!
|
147
|
+
|
148
|
+
author = Author.find_by_name('Uber Guru')
|
149
|
+
author.email # => {"somebody@example.org"=>"personal"}
|
150
|
+
# Perfect!
|
151
|
+
```
|
152
|
+
|
153
|
+
## Versioning
|
154
|
+
|
155
|
+
Semantic versioning (http://semver.org/spec/v2.0.0.html) is used.
|
156
|
+
|
157
|
+
For a version number MAJOR.MINOR.PATCH, unless MAJOR is 0:
|
158
|
+
|
159
|
+
1. MAJOR version is incremented when incompatible API changes are made,
|
160
|
+
2. MINOR version is incremented when functionality is added in a backwards-compatible manner,
|
161
|
+
3. PATCH version is incremented when backwards-compatible bug fixes are made.
|
162
|
+
|
163
|
+
Major version "zero" (0.y.z) is for initial development. Anything may change at any time.
|
164
|
+
The public API should not be considered stable.
|
165
|
+
|
166
|
+
## Dependencies
|
167
|
+
|
168
|
+
- Ruby >= 2.1
|
169
|
+
- Rails >= 4.0
|
170
|
+
- observable_object
|
171
|
+
|
172
|
+
## Contributing
|
173
|
+
|
174
|
+
1. Fork it ( https://github.com/moonfly/store_complex/fork )
|
175
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
176
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
177
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
178
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'store_complex/version'
|
2
|
+
require 'observable_object'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module StoreComplex
|
6
|
+
def self.obj_to_store(value)
|
7
|
+
return nil if value.nil?
|
8
|
+
JSON.generate(value.is_a?(Hash) ? value : Array(value))
|
9
|
+
end
|
10
|
+
def self.store_to_obj(value)
|
11
|
+
return [] if value.nil?
|
12
|
+
JSON.parse(value)
|
13
|
+
end
|
14
|
+
|
15
|
+
module Accessor
|
16
|
+
def store_complex(store_name,*attr_names)
|
17
|
+
attr_names.each do |name|
|
18
|
+
attr_set = name.to_s+'='
|
19
|
+
|
20
|
+
define_method(name) do
|
21
|
+
value = (self.send(store_name) || {})[name.to_s]
|
22
|
+
ObservableObject::deep_wrap( StoreComplex::store_to_obj(value) ) { |obj| self.send(attr_set, obj) }
|
23
|
+
end
|
24
|
+
|
25
|
+
define_method(attr_set) do |value|
|
26
|
+
store = self.send(store_name) || {}
|
27
|
+
store[name.to_s] = StoreComplex::obj_to_store(value)
|
28
|
+
self.send(store_name.to_s+'=',store)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
ActiveRecord.extend StoreComplex::Accessor
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'store_complex/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'store_complex'
|
8
|
+
spec.version = StoreComplex::VERSION
|
9
|
+
spec.authors = ['moonfly (Andrey Pronin)']
|
10
|
+
spec.email = ['moonfly.msk@gmail.com']
|
11
|
+
spec.summary = %q{Store complex data (arrays, hashes) in hstore attributes}
|
12
|
+
spec.description = <<-EOF
|
13
|
+
Stores complex data that includes Arrays and Hashes (possibly nested) in an attribute inside hstore field.
|
14
|
+
The most typical usage scenario is storing arrays in hstore, but it can handle more complex cases.
|
15
|
+
EOF
|
16
|
+
spec.homepage = 'https://github.com/moonfly/store_complex'
|
17
|
+
spec.license = 'MIT'
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0")
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
|
24
|
+
spec.rdoc_options = ['--charset=UTF-8']
|
25
|
+
spec.extra_rdoc_files = %w[README.md CONTRIBUTORS.md LICENSE.md]
|
26
|
+
|
27
|
+
spec.required_ruby_version = '>= 2.1.0'
|
28
|
+
|
29
|
+
spec.add_dependency 'rails', '>= 4.0'
|
30
|
+
spec.add_dependency 'observable_object'
|
31
|
+
|
32
|
+
spec.add_development_dependency 'bundler', '>= 1.6'
|
33
|
+
spec.add_development_dependency 'rake'
|
34
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
35
|
+
spec.add_development_dependency 'coveralls'
|
36
|
+
spec.add_development_dependency 'dotenv'
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: store_complex
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- moonfly (Andrey Pronin)
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: observable_object
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.6'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: dotenv
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: " Stores complex data that includes Arrays and Hashes (possibly nested)
|
112
|
+
in an attribute inside hstore field. \n The most typical usage scenario is storing
|
113
|
+
arrays in hstore, but it can handle more complex cases.\n"
|
114
|
+
email:
|
115
|
+
- moonfly.msk@gmail.com
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files:
|
119
|
+
- README.md
|
120
|
+
- CONTRIBUTORS.md
|
121
|
+
- LICENSE.md
|
122
|
+
files:
|
123
|
+
- ".gitignore"
|
124
|
+
- ".travis.yml"
|
125
|
+
- CHANGELOG.md
|
126
|
+
- CONTRIBUTORS.md
|
127
|
+
- Gemfile
|
128
|
+
- LICENSE.md
|
129
|
+
- README.md
|
130
|
+
- Rakefile
|
131
|
+
- lib/store_complex.rb
|
132
|
+
- lib/store_complex/version.rb
|
133
|
+
- store_complex.gemspec
|
134
|
+
homepage: https://github.com/moonfly/store_complex
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
metadata: {}
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options:
|
140
|
+
- "--charset=UTF-8"
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: 2.1.0
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubyforge_project:
|
155
|
+
rubygems_version: 2.2.2
|
156
|
+
signing_key:
|
157
|
+
specification_version: 4
|
158
|
+
summary: Store complex data (arrays, hashes) in hstore attributes
|
159
|
+
test_files: []
|