schemaless_field 0.0.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d076dbf9241157a017c5ac1518053079c8559553
4
+ data.tar.gz: e190ffdc90808e087b959cd0cae00c595ea8ba92
5
+ SHA512:
6
+ metadata.gz: 1c61bd854ee55720a84564cb3f737188aac2355bc1314e579055fe89882dba47c3c81aae20aceed67fbf40000394452a2aa45ef73df3e47cc5a83b9bf19e8885
7
+ data.tar.gz: d55c2f5d0bb6c40768624e68e10a921693a377bda786e04661a99986bd46ea7006c308c3933e99bbd39128aaff849c79ad9197a12d91b1c95f7cf1e6ef7e29f7
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in schemaless_field.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem 'guard-rspec'
8
+ gem 'guard-rubocop'
9
+ gem 'guard-bundler'
10
+ end
data/Guardfile ADDED
@@ -0,0 +1,20 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :bundler do
5
+ watch('Gemfile')
6
+ # Uncomment next line if your Gemfile contains the `gemspec' command.
7
+ # watch(/^.+\.gemspec/)
8
+ end
9
+
10
+ guard :rspec do
11
+ watch(%r{^spec/.+_spec\.rb$})
12
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
13
+ watch('spec/spec_helper.rb') { "spec" }
14
+ end
15
+
16
+
17
+ # guard :rubocop do
18
+ # watch(%r{.+\.rb$})
19
+ # watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
20
+ # end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Stan Bondi
2
+
3
+ MIT License
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,70 @@
1
+ # SchemalessField
2
+
3
+ Basic accessor methods for schemaless ORM fields.
4
+ For e.g. a JSON field in Postgres.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'schemaless_field'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install schemaless_field
19
+
20
+ ## Usage
21
+
22
+ ```ruby
23
+
24
+ class Item < ActiveRecord::Base # Any class with a json string or hash can be used
25
+ after_initialize :set_data
26
+
27
+ json_attr :data do
28
+ f.field :color, '$..color' # explicit path (not required)
29
+ f.field :array # implicit path
30
+ f.field :nested_field # nested implicit path i.e: $..nested.field
31
+
32
+ f.first_thing, '$..things[0]' # explicit path
33
+ end
34
+
35
+ private
36
+
37
+ def set_data
38
+ self.data ||= {
39
+ color: 'red',
40
+ nested: {
41
+ field: true
42
+ },
43
+ things: [
44
+ {name: 'car'},
45
+ {name: 'laptop'}
46
+ ]
47
+ }
48
+ end
49
+
50
+ end
51
+ ```
52
+
53
+ Now some field accessors will be available
54
+
55
+ ```ruby
56
+
57
+ item = Item.new
58
+ item.color #= "red"
59
+ item.color = "blue" # data = { 'color' => 'blue', ... }
60
+ item.nested_field # true
61
+ item.first_thing # "car"
62
+ ```
63
+
64
+ ## Contributing
65
+
66
+ 1. Fork it ( http://github.com/<my-github-username>/schemaless_field/fork )
67
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
68
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
69
+ 4. Push to the branch (`git push origin my-new-feature`)
70
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/railtie.rb ADDED
@@ -0,0 +1,17 @@
1
+ require "rails"
2
+
3
+ module SchemalessField
4
+ class Railtie < Rails::Railtie # :nodoc:
5
+ config.eager_load_namespaces << SchemalessField
6
+
7
+ initializer "schemaless_field.initialize" do |app|
8
+ ActiveSupport.on_load(:active_record) do
9
+ include SchemalessField::DSL
10
+ end
11
+
12
+ ActiveSupport.on_load(:mongoid) do
13
+ raise "You do not need schemaless_field with mongoid."
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module SchemalessField
2
+ module DSL
3
+ def self.included(base)
4
+ base.class_eval do
5
+ def self.json_attr(attr)
6
+ yield Field.new(self, attr)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,50 @@
1
+ module SchemalessField
2
+ class Field
3
+ attr_reader :model, :model_attr
4
+
5
+ def initialize(model, model_attr)
6
+ @model = model
7
+ @model_attr = model_attr
8
+ end
9
+
10
+ def field(attribute, path = nil)
11
+ path ||= path_from_attr(attribute)
12
+ mod = Module.new
13
+ model.send(:include, mod)
14
+ mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
15
+ # Getter
16
+ def #{attribute}
17
+ ::JsonPath.on(json__#{attribute}, '#{path}')[0]
18
+ end
19
+
20
+ # Setter
21
+ def #{attribute}=(value)
22
+ self.#{@model_attr} = ::JsonPath
23
+ .for(json__#{attribute})
24
+ .gsub('#{path}') { |v| value }
25
+ .to_hash
26
+ end
27
+
28
+ private
29
+
30
+ def json__#{attribute}
31
+ val = self.send(:#{@model_attr})
32
+ case val
33
+ when String
34
+ val
35
+ when Hash
36
+ val.deep_stringify_keys
37
+ end
38
+ end
39
+ RUBY
40
+ end
41
+
42
+ private
43
+
44
+ def path_from_attr(attribute)
45
+ "$..#{attribute.to_s.gsub('_', '.')}"
46
+ rescue
47
+ "$..#{attribute}"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,3 @@
1
+ module SchemalessField
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,10 @@
1
+ require 'jsonpath'
2
+ require 'json'
3
+
4
+ require "schemaless_field/version"
5
+ require 'schemaless_field/dsl'
6
+ require 'schemaless_field/field'
7
+
8
+ require 'active_support/core_ext/hash'
9
+
10
+ require 'railtie' if defined?(Rails)
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'schemaless_field/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "schemaless_field"
8
+ spec.version = SchemalessField::VERSION
9
+ spec.authors = ["Stan Bondi"]
10
+ spec.email = ["stan@fixate.it"]
11
+ spec.summary = %q{Basic accessor methods for schemaless ORM fields.}
12
+ spec.description = <<-TXT
13
+ Basic accessor methods for schemaless ORM fields.
14
+ For e.g. a JSON field in Postgres.
15
+ TXT
16
+ spec.homepage = ""
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.add_dependency "jsonpath", "~> 0.5.6"
25
+ spec.add_dependency "activesupport", "> 2"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.5"
28
+ spec.add_development_dependency "rake"
29
+ end
data/spec/dsl_spec.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ class Dummy
4
+ attr_accessor :data
5
+ include SchemalessField::DSL
6
+
7
+ def initialize
8
+ self.data = {
9
+ lannisters: {
10
+ geoffrey: 'king',
11
+ cersei: 'queen regent',
12
+ tyrion: 'master of coin'
13
+ }
14
+ }
15
+ end
16
+ end
17
+
18
+ describe SchemalessField::DSL do
19
+ it 'creates and yields a new field' do
20
+ Dummy.json_attr :data do |f|
21
+ f.field :lannisters
22
+ end
23
+
24
+ dummy = Dummy.new
25
+ expect(dummy.lannisters).to eq(dummy.data['lannisters'])
26
+ end
27
+ end
28
+
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ class Dummy
4
+ attr_accessor :data
5
+
6
+ def initialize
7
+ self.data = {
8
+ foo: [1,2,3],
9
+ bar: '!!!',
10
+ nested: {
11
+ quite: 'deep'
12
+ },
13
+ complex: [
14
+ # These MUST BE string keys
15
+ {'name' => 'another', 'value' => 100},
16
+ {'name' => 'test', 'value' => 123}
17
+ ]
18
+ }
19
+ end
20
+ end
21
+
22
+ describe SchemalessField::Field do
23
+ subject { Dummy.new }
24
+ let(:field) { described_class.new(subject.class, :data) }
25
+
26
+ before { field.field :foo, '$..foo' }
27
+
28
+ it 'defines getter method on the model' do
29
+ expect(subject).to respond_to(:foo)
30
+ end
31
+
32
+ it 'defines getter method on the model' do
33
+ expect(subject).to respond_to(:"foo=")
34
+ end
35
+
36
+ it 'returns nil if path doesnt exist' do
37
+ field.field :not_existing_path
38
+ expect(subject.not_existing_path).to be_nil
39
+ end
40
+
41
+ it 'returns value from complex path' do
42
+ field.field :complex, '$..complex[?(@.name=="test")].value'
43
+ expect(subject.complex).to eq(123)
44
+ end
45
+
46
+ describe 'implicit path' do
47
+ before { field.field :nested_quite }
48
+ it 'finds field implicitly' do
49
+ expect(subject.nested_quite).to eq('deep')
50
+ end
51
+ end
52
+
53
+ describe 'getter' do
54
+ it 'gets data from the json' do
55
+ expect(subject.foo).to eq([1,2,3])
56
+ end
57
+
58
+ it 'gets deep data in json' do
59
+ field.field :nested_deep, '$..nested.quite'
60
+ expect(subject.nested_deep).to eq('deep')
61
+ end
62
+ end
63
+
64
+ describe 'setter' do
65
+ it 'sets data in json' do
66
+ subject.foo = 'haldo'
67
+ expect(subject.data['foo']).to eq('haldo')
68
+ end
69
+
70
+ it 'sets deep data in json' do
71
+ field.field :nested_deep, '$..nested.quite'
72
+ subject.nested_deep = 'shallow'
73
+ expect(subject.data['nested']['quite']).to eq('shallow')
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,21 @@
1
+ $:.unshift(File.expand_path('../../lib', __FILE__))
2
+
3
+ require 'schemaless_field'
4
+
5
+ # This file was generated by the `rspec --init` command. Conventionally, all
6
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
7
+ # Require this file using `require "spec_helper"` to ensure that it is only
8
+ # loaded once.
9
+ #
10
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
11
+ RSpec.configure do |config|
12
+ config.treat_symbols_as_metadata_keys_with_true_values = true
13
+ config.run_all_when_everything_filtered = true
14
+ config.filter_run :focus
15
+
16
+ # Run specs in random order to surface order dependencies. If you find an
17
+ # order dependency and want to debug it, you can fix the order by providing
18
+ # the seed, which is printed after each run.
19
+ # --seed 1234
20
+ config.order = 'random'
21
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: schemaless_field
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Stan Bondi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jsonpath
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.5.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.5.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">"
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">"
39
+ - !ruby/object:Gem::Version
40
+ version: '2'
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.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
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
+ description: |2
70
+ Basic accessor methods for schemaless ORM fields.
71
+ For e.g. a JSON field in Postgres.
72
+ email:
73
+ - stan@fixate.it
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".gitignore"
79
+ - ".rspec"
80
+ - Gemfile
81
+ - Guardfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - lib/railtie.rb
86
+ - lib/schemaless_field.rb
87
+ - lib/schemaless_field/dsl.rb
88
+ - lib/schemaless_field/field.rb
89
+ - lib/schemaless_field/version.rb
90
+ - schemaless_field.gemspec
91
+ - spec/dsl_spec.rb
92
+ - spec/field_spec.rb
93
+ - spec/spec_helper.rb
94
+ homepage: ''
95
+ licenses:
96
+ - MIT
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.2.0
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: Basic accessor methods for schemaless ORM fields.
118
+ test_files:
119
+ - spec/dsl_spec.rb
120
+ - spec/field_spec.rb
121
+ - spec/spec_helper.rb
122
+ has_rdoc: