yesql 0.1.8 → 0.2.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 +4 -4
- data/README.md +6 -22
- data/lib/.rbnext/2.3/yesql/errors/file_path_does_not_exist_error.rb +40 -0
- data/lib/.rbnext/2.3/yesql/errors/no_bindings_provided_error.rb +38 -0
- data/lib/.rbnext/2.3/yesql/errors/output_argument_error.rb +31 -0
- data/lib/.rbnext/2.3/yesql/query/transform_result.rb +48 -0
- data/lib/.rbnext/2.7/yesql/bindings/extract.rb +71 -0
- data/lib/.rbnext/2.7/yesql/bindings/extractor.rb +38 -0
- data/lib/.rbnext/2.7/yesql/bindings/transformed.rb +58 -0
- data/lib/.rbnext/2.7/yesql/bindings/utils.rb +16 -0
- data/lib/.rbnext/2.7/yesql/errors/file_path_does_not_exist_error.rb +43 -0
- data/lib/.rbnext/2.7/yesql/errors/no_bindings_provided_error.rb +41 -0
- data/lib/.rbnext/2.7/yesql/query/transform_result.rb +48 -0
- data/lib/.rbnext/2.7/yesql/statement.rb +44 -0
- data/lib/.rbnext/2.7/yesql/utils/read.rb +20 -0
- data/lib/.rbnext/3.0/yesql/bindings/extract.rb +71 -0
- data/lib/.rbnext/3.0/yesql/bindings/transformed.rb +58 -0
- data/lib/.rbnext/3.0/yesql/common/adapter.rb +18 -0
- data/lib/.rbnext/3.0/yesql/config/configuration.rb +32 -0
- data/lib/.rbnext/3.0/yesql/errors/file_path_does_not_exist_error.rb +43 -0
- data/lib/.rbnext/3.0/yesql/errors/no_bindings_provided_error.rb +41 -0
- data/lib/.rbnext/3.0/yesql/params/output.rb +26 -0
- data/lib/.rbnext/3.0/yesql/query/performer.rb +44 -0
- data/lib/.rbnext/3.0/yesql/query/result.rb +41 -0
- data/lib/.rbnext/3.0/yesql/query/transform_result.rb +48 -0
- data/lib/.rbnext/3.0/yesql/statement.rb +44 -0
- data/lib/.rbnext/3.0/yesql/utils/read.rb +20 -0
- data/lib/yesql.rb +25 -31
- data/lib/yesql/bindings/extract.rb +15 -21
- data/lib/yesql/bindings/extractor.rb +21 -21
- data/lib/yesql/bindings/transformed.rb +8 -11
- data/lib/yesql/bindings/utils.rb +7 -5
- data/lib/yesql/common/adapter.rb +6 -13
- data/lib/yesql/config/configuration.rb +4 -5
- data/lib/yesql/errors/file_path_does_not_exist_error.rb +22 -17
- data/lib/yesql/errors/no_bindings_provided_error.rb +18 -19
- data/lib/yesql/errors/output_argument_error.rb +13 -4
- data/lib/yesql/params/output.rb +6 -14
- data/lib/yesql/query/performer.rb +13 -42
- data/lib/yesql/query/result.rb +12 -17
- data/lib/yesql/query/transform_result.rb +11 -21
- data/lib/yesql/statement.rb +19 -24
- data/lib/yesql/utils/read.rb +11 -8
- data/lib/yesql/version.rb +1 -1
- metadata +47 -17
- data/.gitignore +0 -4
- data/.rubocop.yml +0 -20
- data/Gemfile +0 -9
- data/Gemfile.lock +0 -167
- data/Rakefile +0 -1
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/lib/yesql/errors/cache_expiration_error.rb +0 -18
- data/yesql.gemspec +0 -28
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yesql/utils/read"
|
4
|
+
|
5
|
+
module ::YeSQL
|
6
|
+
module Errors
|
7
|
+
class NoBindingsProvidedError
|
8
|
+
include ::YeSQL::Utils::Read
|
9
|
+
|
10
|
+
def initialize(binds, file_path)
|
11
|
+
@binds = binds
|
12
|
+
@file_path = file_path
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate_statement_bindings
|
16
|
+
return unless statement_binds.size.positive?
|
17
|
+
return if expected_binds?
|
18
|
+
|
19
|
+
raise ::ArgumentError, format(MESSAGE, renderable_statement_binds)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :binds, :file_path
|
25
|
+
|
26
|
+
MESSAGE = <<~MSG
|
27
|
+
|
28
|
+
YeSQL invoked without bindings.
|
29
|
+
|
30
|
+
Expected bindings are:
|
31
|
+
|
32
|
+
%s
|
33
|
+
MSG
|
34
|
+
private_constant :MESSAGE
|
35
|
+
|
36
|
+
def statement_binds ; statement.scan(::YeSQL::BIND_REGEX).tap { |_1| break (_1 || []).sort }; end
|
37
|
+
def renderable_statement_binds ; statement_binds.flatten.map { |_1| "- `#{_1}`\n" }.join; end
|
38
|
+
def expected_binds? ; binds.is_a?(::Hash) && !binds.empty?; end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yesql"
|
4
|
+
require "yesql/common/adapter"
|
5
|
+
require "forwardable"
|
6
|
+
|
7
|
+
module ::YeSQL
|
8
|
+
module Query
|
9
|
+
class TransformResult
|
10
|
+
extend ::Forwardable
|
11
|
+
|
12
|
+
include ::YeSQL::Common::Adapter
|
13
|
+
|
14
|
+
def initialize(output:, result:)
|
15
|
+
@output = output
|
16
|
+
@result = result
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
if rails_5? && mysql?
|
21
|
+
return columns if columns?
|
22
|
+
return rows_values if rows?
|
23
|
+
return array_of_symbol_hashes if hash?
|
24
|
+
end
|
25
|
+
|
26
|
+
return result.public_send(output.to_sym) if columns? || rows?
|
27
|
+
|
28
|
+
to_a.map(&:symbolize_keys)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :output, :result
|
34
|
+
|
35
|
+
def_delegators(:result, :fields, :rows, :to_a)
|
36
|
+
def_delegators(:output, :columns?, :hash?, :rows?)
|
37
|
+
|
38
|
+
def array_of_symbol_hashes
|
39
|
+
to_a.tap { break hashed_rows(_1) if rails_5? }.map { |_1| _1&.symbolize_keys || _1 }
|
40
|
+
end
|
41
|
+
|
42
|
+
def columns ; fields || result.columns; end
|
43
|
+
def rows_values ; to_a.map { |_1| _1.respond_to?(:values) ? _1.values : _1 }; end
|
44
|
+
def hashed_rows(rows) ; rows.map { |_1| columns.zip(_1).to_h }; end
|
45
|
+
def rails_5? ; ::ActiveRecord::VERSION::MAJOR == 5; end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
|
5
|
+
require "forwardable"
|
6
|
+
|
7
|
+
require "yesql/utils/read"
|
8
|
+
require "yesql/bindings/extractor"
|
9
|
+
require "yesql/common/adapter"
|
10
|
+
|
11
|
+
module ::YeSQL
|
12
|
+
class Statement
|
13
|
+
extend ::Forwardable
|
14
|
+
|
15
|
+
include ::YeSQL::Common::Adapter
|
16
|
+
include ::YeSQL::Utils::Read
|
17
|
+
# Give access to the quote method.
|
18
|
+
include ::ActiveRecord::ConnectionAdapters::Quoting
|
19
|
+
|
20
|
+
def initialize(bindings = {}, file_path)
|
21
|
+
@bindings = bindings
|
22
|
+
@file_path = file_path
|
23
|
+
end
|
24
|
+
|
25
|
+
def bound
|
26
|
+
to_s.gsub(::YeSQL::BIND_REGEX) { |_1| extract_bind_values(extractor[_1[/(\w+)/].to_sym]) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s ; @to_s ||= statement(readable: true); end
|
30
|
+
def view? ; to_s =~ /^create\s.*view\s/i; end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :bindings, :file_path
|
35
|
+
|
36
|
+
def extract_bind_values(match)
|
37
|
+
return quote(match[:value]) if view?
|
38
|
+
|
39
|
+
match[:bind][:vars]
|
40
|
+
end
|
41
|
+
|
42
|
+
def extractor ; @extractor ||= ::YeSQL::Bindings::Extractor.new(bindings: bindings).call; end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ::YeSQL
|
4
|
+
module Utils
|
5
|
+
module Read
|
6
|
+
def statement(readable: false) ; read_file(found_file, readable); end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def read_file(file, readable)
|
11
|
+
return File.readlines(file, chomp: true).join(" ") if readable == true
|
12
|
+
|
13
|
+
File.read(file)
|
14
|
+
end
|
15
|
+
|
16
|
+
def found_file ; dir_sql_files.find { |_1| _1.include?("#{file_path}.sql") }; end
|
17
|
+
def dir_sql_files ; Dir["./#{::YeSQL.config.path}/**/*.sql"]; end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yesql"
|
4
|
+
require "yesql/common/adapter"
|
5
|
+
|
6
|
+
module ::YeSQL
|
7
|
+
module Bindings
|
8
|
+
class Extract
|
9
|
+
include ::YeSQL::Common::Adapter
|
10
|
+
|
11
|
+
def initialize(hash, indexed_bindings, index, value)
|
12
|
+
@hash = hash
|
13
|
+
@index = index
|
14
|
+
@indexed_bindings = indexed_bindings
|
15
|
+
@value = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def bind_vals
|
19
|
+
return [nil, value] unless array?
|
20
|
+
|
21
|
+
value.map { [nil, _1] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def bind_vars
|
25
|
+
if mysql?
|
26
|
+
return "?" unless array?
|
27
|
+
|
28
|
+
Array.new(size, "?").join(", ")
|
29
|
+
elsif pg?
|
30
|
+
return "$#{last_val}" unless array?
|
31
|
+
|
32
|
+
value.map.with_index(bind_index) { "$#{_2}" }.join(", ")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def prev
|
37
|
+
return if first?
|
38
|
+
|
39
|
+
indexed_bindings[index - 2].first
|
40
|
+
end
|
41
|
+
|
42
|
+
def last_val ; prev_last_val + current_val_size; end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :hash, :index, :indexed_bindings, :value
|
47
|
+
|
48
|
+
def current_val_size
|
49
|
+
return size if array?
|
50
|
+
|
51
|
+
1
|
52
|
+
end
|
53
|
+
|
54
|
+
def prev_last_val
|
55
|
+
return 0 if first?
|
56
|
+
|
57
|
+
hash[prev][:last_val]
|
58
|
+
end
|
59
|
+
|
60
|
+
def bind_index
|
61
|
+
return 1 if first?
|
62
|
+
|
63
|
+
prev_last_val + 1
|
64
|
+
end
|
65
|
+
|
66
|
+
def size ; value.size; end
|
67
|
+
def first? ; index == 1; end
|
68
|
+
def array? ; value.is_a?(Array); end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yesql"
|
4
|
+
require "yesql/common/adapter"
|
5
|
+
|
6
|
+
module ::YeSQL
|
7
|
+
module Bindings
|
8
|
+
class Transformed
|
9
|
+
include ::YeSQL::Common::Adapter
|
10
|
+
|
11
|
+
def initialize(statement_binds:)
|
12
|
+
@statement_binds = statement_binds
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
return mysql_rails5_binds if rails5? && mysql?
|
17
|
+
return mysql_binds if !rails5? && mysql?
|
18
|
+
|
19
|
+
pg_binds
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :statement_binds
|
25
|
+
|
26
|
+
def rails5? ; ::ActiveRecord::VERSION::MAJOR == 5; end
|
27
|
+
|
28
|
+
def mysql_rails5_binds
|
29
|
+
statement_binds
|
30
|
+
.flat_map(&:first)
|
31
|
+
.each_slice(2)
|
32
|
+
.flat_map do
|
33
|
+
next [_1, _2].compact.map(&:last) if _1.is_a?(Array)
|
34
|
+
|
35
|
+
_2
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def mysql_binds
|
40
|
+
statement_binds
|
41
|
+
.map(&:first)
|
42
|
+
.flatten
|
43
|
+
.each_slice(2)
|
44
|
+
.to_a
|
45
|
+
end
|
46
|
+
|
47
|
+
def pg_binds
|
48
|
+
statement_binds
|
49
|
+
.sort_by { _2.to_s.tr("$", "").to_i }
|
50
|
+
.uniq
|
51
|
+
.map(&:first)
|
52
|
+
.flatten
|
53
|
+
.each_slice(2)
|
54
|
+
.to_a
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module ::YeSQL
|
6
|
+
module Common
|
7
|
+
module Adapter
|
8
|
+
extend ::Forwardable
|
9
|
+
|
10
|
+
def mysql? ; adapter == "Mysql2"; end
|
11
|
+
def pg? ; adapter == "PostgreSQL"; end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def adapter ; ::ActiveRecord::Base.connection.adapter_name; end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ::YeSQL
|
4
|
+
module Config
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :connection, :path
|
7
|
+
|
8
|
+
DEFAULT_PATH = "app/yesql"
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@connection = ""
|
12
|
+
@path = DEFAULT_PATH
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def config ; @config ||= ::YeSQL::Configuration.new; end
|
19
|
+
|
20
|
+
def configure
|
21
|
+
yield config if block_given?
|
22
|
+
end
|
23
|
+
|
24
|
+
def reset_config
|
25
|
+
tap do |conf|
|
26
|
+
conf.configure do |configuration|
|
27
|
+
configuration.path = ::YeSQL::Configuration::DEFAULT_PATH
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ::YeSQL
|
4
|
+
module Errors
|
5
|
+
class FilePathDoesNotExistError
|
6
|
+
def initialize(file_path)
|
7
|
+
@file_path = file_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate_file_path_existence
|
11
|
+
return if file_exists?
|
12
|
+
|
13
|
+
raise(
|
14
|
+
::NotImplementedError,
|
15
|
+
format(
|
16
|
+
MESSAGE,
|
17
|
+
available_files: available_files,
|
18
|
+
file_path: file_path,
|
19
|
+
path: ::YeSQL.config.path
|
20
|
+
)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :file_path
|
27
|
+
|
28
|
+
MESSAGE = <<~MSG
|
29
|
+
|
30
|
+
SQL file "%<file_path>s" does not exist in %<path>s.
|
31
|
+
|
32
|
+
Available SQL files are:
|
33
|
+
|
34
|
+
%<available_files>s
|
35
|
+
MSG
|
36
|
+
private_constant :MESSAGE
|
37
|
+
|
38
|
+
def file_exists? ; path_files.any? { _1.include?("#{file_path}.sql") }; end
|
39
|
+
def path_files ; @path_files ||= Dir["#{::YeSQL.config.path}/**/*.sql"]; end
|
40
|
+
def available_files ; path_files.map { "- #{_1}\n" }.join; end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yesql/utils/read"
|
4
|
+
|
5
|
+
module ::YeSQL
|
6
|
+
module Errors
|
7
|
+
class NoBindingsProvidedError
|
8
|
+
include ::YeSQL::Utils::Read
|
9
|
+
|
10
|
+
def initialize(binds, file_path)
|
11
|
+
@binds = binds
|
12
|
+
@file_path = file_path
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate_statement_bindings
|
16
|
+
return unless statement_binds.size.positive?
|
17
|
+
return if expected_binds?
|
18
|
+
|
19
|
+
raise ::ArgumentError, format(MESSAGE, renderable_statement_binds)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :binds, :file_path
|
25
|
+
|
26
|
+
MESSAGE = <<~MSG
|
27
|
+
|
28
|
+
YeSQL invoked without bindings.
|
29
|
+
|
30
|
+
Expected bindings are:
|
31
|
+
|
32
|
+
%s
|
33
|
+
MSG
|
34
|
+
private_constant :MESSAGE
|
35
|
+
|
36
|
+
def statement_binds ; statement.scan(::YeSQL::BIND_REGEX).tap { break (_1 || []).sort }; end
|
37
|
+
def renderable_statement_binds ; statement_binds.flatten.map { "- `#{_1}`\n" }.join; end
|
38
|
+
def expected_binds? ; binds.is_a?(::Hash) && !binds.empty?; end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yesql"
|
4
|
+
require "forwardable"
|
5
|
+
|
6
|
+
module ::YeSQL
|
7
|
+
module Params
|
8
|
+
class Output
|
9
|
+
extend ::Forwardable
|
10
|
+
|
11
|
+
def initialize(output)
|
12
|
+
@output = output.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def columns? ; output == "columns"; end
|
16
|
+
def rows? ; output == "rows"; end
|
17
|
+
def hash? ; output == "hash"; end
|
18
|
+
|
19
|
+
def_delegator(:output, :to_sym)
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :output
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|