store_complex 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ cache: bundler
3
+
4
+ rvm:
5
+ - 2.1.2
6
+
7
+ branches:
8
+ only:
9
+ - master
10
+
11
+ script: bundle exec rake spec
12
+
13
+ before_install:
14
+ - gem update --system
15
+ - gem --version
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in store_complex.gemspec
4
+ gemspec
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,5 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run RSpec tests (use rake spec SPEC_OPTS='...' to pass rspec options)"
5
+ RSpec::Core::RakeTask.new(:spec)
@@ -0,0 +1,3 @@
1
+ module StoreComplex
2
+ VERSION = "0.1.0"
3
+ end
@@ -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: []