sequel-mysql_json 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 710f4deced669859204c62148fe36c3d2bcfc7ed
4
+ data.tar.gz: 80faad18d63ea8283daac633fc8add7927b4486b
5
+ SHA512:
6
+ metadata.gz: 3ab3341f8495af5051f04db2264bc4772eec821da51922cbe5fbfcfe99c9abb353b312c94839e00340cb412b0f3ff322c7bdea1bdc675bcc3d463ce5e95829c8
7
+ data.tar.gz: 09247d4c9d2a2032b80f4cecd9163bdcbfcb70f3820df44a7cee17c81af2a534242c0bd050f3718e160995fbff909e97f536bbf65a05c00e47bb8d1b6d0988f3
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.swp
11
+ *.swo
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sequel-mysql_json.gemspec
4
+ gemspec
@@ -0,0 +1,154 @@
1
+ # Sequel::MysqlJson
2
+
3
+ Sequel extension and plugin that adds better support for MySQL JSON columns and
4
+ functions (added first on MySQL 5.7.8).
5
+
6
+ ### `mysql_json` plugin
7
+
8
+ `mysql_json` detects MySQL json columns on models and automatically adds column
9
+ accessors that deserializes JSON values. Uses Sequel's builtin Serialization
10
+ plugin for this purpose.
11
+
12
+ ### `mysql_json_op` extension
13
+
14
+ `mysql_json_op` extension adds support to Sequel's DSL to make it easier to
15
+ call MySQL JSON functions and operators.
16
+
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```ruby
23
+ gem 'sequel-mysql_json'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install sequel-mysql_json
33
+
34
+
35
+ ## Usage
36
+
37
+ To enable plugin for all models, call `Sequel::Model.plugin :mysql_json`.
38
+ To enable extension, call `Sequel.extension :mysql_json_ops`.
39
+
40
+ For example, suppose you have a model with a json column `metadata`, like this:
41
+
42
+ ```ruby
43
+ class Thing < Sequel::Model
44
+ set_schema do
45
+ primary_key :id
46
+ json :metadata
47
+ end
48
+ end
49
+
50
+ Thing.create_table!
51
+ ```
52
+
53
+ Because plugin uses the
54
+ [Serialization](http://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/Serialization.html)
55
+ plugin, we can pass serializable Ruby objects, like a hash when setting the
56
+ `metadata` column accessor.
57
+
58
+ ```ruby
59
+ Thing.create(metadata: { foo: 1, bar: 2 })
60
+ # => #<Thing @values={:id=>1, :metadata=>"{\"bar\": 2, \"foo\": 1}"}>
61
+
62
+ Thing.first.metadata['foo']
63
+ # => 1
64
+ ```
65
+
66
+ To construct queries using JSON related functions, first build a `JSONOp`
67
+ object:
68
+
69
+ ```ruby
70
+ Sequel.mysql_json_op(:metadata)
71
+ # => #<Sequel::Mysql::JSONOp @value=>:metadata>
72
+
73
+ Thing.select_map(Sequel.mysql_json_op(:metadata).extract('.foo'))
74
+ # SELECT JSON_EXTRACT(`metadata`, '$.foo') AS `v` FROM `things`
75
+ # => ["1"]
76
+ ```
77
+
78
+ As you can see the `$` prefix is appended automatically to the path selector.
79
+
80
+ If you are using Sequel `core_extension` or `core_refinements`, you can also:
81
+
82
+ ```ruby
83
+ Thing.select_map(:metadata.mysql_json_op.extract('.foo'))
84
+ # SELECT JSON_EXTRACT(`metadata`, '$.foo') AS `v` FROM `things`
85
+ # => ["1"]
86
+ ```
87
+
88
+ `#[]` and `#get` are aliases of `#extract`. Also, when providing a Symbol,
89
+ selector is converted to a path that extracts a field from a JSON object.
90
+ Likewise, when using an Integer, path selector extracts a value from a JSON
91
+ array:
92
+
93
+ ```ruby
94
+ Thing.select_map(:metadata.mysql_json_op[:foo])
95
+ # SELECT JSON_EXTRACT(`metadata`, '$.foo') AS `v` FROM `things`
96
+ # => ["1"]
97
+
98
+ Thing.select_map(:metadata.mysql_json_op[42])
99
+ # SELECT JSON_EXTRACT(`metadata`, '$[1]') AS `v` FROM `things`
100
+ # => [nil]
101
+ ```
102
+
103
+ JSONOp will merge nested `#extract` calls into a single one:
104
+
105
+ ```ruby
106
+ Thing.create(metadata: 5.times.map { |id| { id: id, value: "id#{id}" } })
107
+
108
+ Thing.select_map(:metadata.mysql_json_op['[*]'][:value])
109
+ # SELECT JSON_EXTRACT(`metadata`, '$[*].value') AS `v` FROM `posts`
110
+ # => ["id0", "id1", "id2", "id3", "id4"]
111
+ ```
112
+
113
+ ## Development
114
+
115
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
116
+ `rake test` to run the tests. You can also run `bin/console` for an interactive
117
+ prompt that will allow you to experiment.
118
+
119
+ To install this gem onto your local machine, run `bundle exec rake install`. To
120
+ release a new version, update the version number in `version.rb`, and then run
121
+ `bundle exec rake release`, which will create a git tag for the version, push
122
+ git commits and tags, and push the `.gem` file to
123
+ [rubygems.org](https://rubygems.org).
124
+
125
+
126
+ ## Contributing
127
+
128
+ Bug reports and pull requests are welcome on GitHub at
129
+ https://github.com/munshkr/sequel-mysql_json.
130
+
131
+
132
+ ## License
133
+
134
+ MIT License
135
+
136
+ Copyright (c) 2016 Damián Silvani
137
+
138
+ Permission is hereby granted, free of charge, to any person obtaining a copy
139
+ of this software and associated documentation files (the "Software"), to deal
140
+ in the Software without restriction, including without limitation the rights
141
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
142
+ copies of the Software, and to permit persons to whom the Software is
143
+ furnished to do so, subject to the following conditions:
144
+
145
+ The above copyright notice and this permission notice shall be included in all
146
+ copies or substantial portions of the Software.
147
+
148
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
149
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
150
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
151
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
152
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
153
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
154
+ SOFTWARE.
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:spec) do |t|
5
+ t.libs << "spec"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['spec/**/*_spec.rb']
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sequel/mysql_json"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,159 @@
1
+ module Sequel
2
+ module Mysql
3
+ # The JSONOp class is a simple container for a single object that defines
4
+ # methods that yield Sequel expression objects representing MySQL json
5
+ # operators and functions.
6
+ #
7
+ # In the method documentation examples, assume that:
8
+ #
9
+ # json_op = Sequel.mysql_json_op(:json)
10
+ #
11
+ class JSONOp < Sequel::SQL::Wrapper
12
+ SPACE_RE = /\s+/
13
+
14
+ # Extract a value as json
15
+ #
16
+ # json_op.extract('[0]') # JSON_EXTRACT(json, '$[0]')
17
+ # json_op.extract('.a') # JSON_EXTRACT(json, '$.a')
18
+ #
19
+ # When using an Integer, it will be used as an index of a JSON array
20
+ #
21
+ # json_op.extract(0) # JSON_EXTRACT(json, '$[0]')
22
+ #
23
+ # When using a Symbol, it will be used as a member of a JSON object
24
+ #
25
+ # json_op.extract(:a) # JSON_EXTRACT(json, '$.a')
26
+ #
27
+ def extract(key)
28
+ case value
29
+ when SQL::Function
30
+ # Merge path selector of a nested :extract function
31
+ json_op, path = value.args
32
+ json_op(:extract, json_op, path + path_selector(key))
33
+ else
34
+ json_op(:extract, self, "$#{path_selector(key)}")
35
+ end
36
+ end
37
+ alias :[] :extract
38
+ alias :get :extract
39
+
40
+ # Replace values for paths that exist and add values
41
+ # for paths that do not exist.
42
+ #
43
+ def set(*args)
44
+ json_op(:set, self, *args)
45
+ end
46
+
47
+ # Add new values but does not replace existing values
48
+ #
49
+ def insert(*args)
50
+ json_op(:insert, self, *args)
51
+ end
52
+
53
+ # Replace existing values and ignore new values
54
+ #
55
+ def replace(*args)
56
+ json_op(:replace, self, *args)
57
+ end
58
+
59
+ # Take a JSON document and one or more paths that specify values
60
+ # to be removed from the document.
61
+ #
62
+ def remove(*args)
63
+ json_op(:remove, self, *args)
64
+ end
65
+
66
+ # Take two or more JSON documents and return the combined result
67
+ #
68
+ def merge(*args)
69
+ json_op(:merge, self, *args)
70
+ end
71
+
72
+ # Return the value's JSON type if it is valid and produces an error
73
+ # otherwise.
74
+ #
75
+ def type
76
+ Sequel::SQL::StringExpression.new(:NOOP, function(:type, self))
77
+ end
78
+
79
+ private
80
+
81
+ # Return a function wrapped in a JSONOp object
82
+ def json_op(*args)
83
+ self.class.new(function(*args))
84
+ end
85
+
86
+ # Return a function with the given name, and the receiver as the first
87
+ # argument, with any additional arguments given.
88
+ def function(name, *args)
89
+ SQL::Function.new(function_name(name), *args)
90
+ end
91
+
92
+ # The json type functions are prefixed with JSON_
93
+ def function_name(name)
94
+ "JSON_#{name.to_s.upcase}"
95
+ end
96
+
97
+ # Return a path selector based on key class
98
+ def path_selector(key)
99
+ case key
100
+ when Integer
101
+ "[#{key}]"
102
+ when Symbol
103
+ ".#{quote(key)}"
104
+ else
105
+ key
106
+ end
107
+ end
108
+
109
+ # Return quoted key if it has spaces
110
+ def quote(key)
111
+ key.to_s.index(SPACE_RE) ? "\"#{key}\"" : key
112
+ end
113
+ end
114
+
115
+ module JSONOpMethods
116
+ # Wrap the receiver in an JSONOp so you can easily use the MySQL
117
+ # json functions and operators with it.
118
+ def mysql_json_op
119
+ JSONOp.new(self)
120
+ end
121
+ end
122
+ end
123
+
124
+ module SQL::Builders
125
+ # Return the object wrapped in an Mysql::JSONOp.
126
+ def mysql_json_op(v)
127
+ case v
128
+ when Mysql::JSONOp
129
+ v
130
+ else
131
+ Mysql::JSONOp.new(v)
132
+ end
133
+ end
134
+ end
135
+
136
+ class SQL::GenericExpression
137
+ include Sequel::Mysql::JSONOpMethods
138
+ end
139
+
140
+ class LiteralString
141
+ include Sequel::Mysql::JSONOpMethods
142
+ end
143
+ end
144
+
145
+ # :nocov:
146
+ if Sequel.core_extensions?
147
+ class Symbol
148
+ include Sequel::Mysql::JSONOpMethods
149
+ end
150
+ end
151
+
152
+ if defined?(Sequel::CoreRefinements)
153
+ module Sequel::CoreRefinements
154
+ refine Symbol do
155
+ include Sequel::Mysql::JSONOpMethods
156
+ end
157
+ end
158
+ end
159
+ # :nocov:
@@ -0,0 +1,14 @@
1
+ module Sequel
2
+ module Plugins
3
+ module MysqlJson
4
+ module ClassMethods
5
+ def set_dataset(*args)
6
+ super
7
+ @db_schema.select { |_, o| o[:db_type] == 'json' }.each_key do |c|
8
+ plugin :serialization, :json, c
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "sequel-mysql_json"
7
+ spec.version = "0.1.0"
8
+ spec.authors = ["Damián Silvani"]
9
+ spec.email = ["munshkr@gmail.com"]
10
+
11
+ spec.summary = %q{Sequel extension and plugin that adds support for MySQL JSON columns.}
12
+ spec.description = %q{
13
+ Extension adds support to Sequel's DSL to make it easier to call MySQL JSON
14
+ function and operators (added first on MySQL 5.7.8).
15
+
16
+ Plugin detects MySQL json columns on models and adds column accessor that
17
+ deserializes JSON values automatically (using Sequel's builtin Serialization
18
+ plugin).
19
+ }
20
+ spec.homepage = "https://github.com/munshkr/sequel-mysql_json"
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ["lib"]
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.12"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "minitest", "~> 5.0"
30
+
31
+ spec.add_dependency 'sequel', '>= 4'
32
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sequel-mysql_json
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Damián Silvani
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sequel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '4'
69
+ description: "\n Extension adds support to Sequel's DSL to make it easier to call
70
+ MySQL JSON\n function and operators (added first on MySQL 5.7.8).\n\n Plugin
71
+ detects MySQL json columns on models and adds column accessor that\n deserializes
72
+ JSON values automatically (using Sequel's builtin Serialization\n plugin).\n
73
+ \ "
74
+ email:
75
+ - munshkr@gmail.com
76
+ executables: []
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - ".gitignore"
81
+ - ".travis.yml"
82
+ - Gemfile
83
+ - README.md
84
+ - Rakefile
85
+ - bin/console
86
+ - bin/setup
87
+ - lib/sequel/extensions/mysql_json_ops.rb
88
+ - lib/sequel/plugins/mysql_json.rb
89
+ - sequel-mysql_json.gemspec
90
+ homepage: https://github.com/munshkr/sequel-mysql_json
91
+ licenses: []
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.5.1
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Sequel extension and plugin that adds support for MySQL JSON columns.
113
+ test_files: []