sequel-mysql_json 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []