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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +154 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/sequel/extensions/mysql_json_ops.rb +159 -0
- data/lib/sequel/plugins/mysql_json.rb +14 -0
- data/sequel-mysql_json.gemspec +32 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -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.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -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
|
data/bin/setup
ADDED
@@ -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,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: []
|