yesql 0.1.4 → 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 +14 -31
- 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 -32
- data/lib/yesql/bindings/extract.rb +22 -19
- data/lib/yesql/bindings/extractor.rb +21 -21
- data/lib/yesql/bindings/transformed.rb +58 -0
- data/lib/yesql/bindings/utils.rb +7 -5
- data/lib/yesql/common/adapter.rb +18 -0
- 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 +20 -19
- data/lib/yesql/errors/output_argument_error.rb +13 -4
- data/lib/yesql/params/output.rb +26 -0
- data/lib/yesql/query/performer.rb +20 -62
- data/lib/yesql/query/result.rb +41 -0
- data/lib/yesql/query/transform_result.rb +48 -0
- data/lib/yesql/statement.rb +44 -0
- data/lib/yesql/utils/read.rb +11 -8
- data/lib/yesql/version.rb +1 -1
- metadata +68 -18
- data/.gitignore +0 -1
- data/Gemfile +0 -9
- data/Gemfile.lock +0 -165
- data/Rakefile +0 -1
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/lib/yesql/bindings/binder.rb +0 -19
- data/lib/yesql/errors/cache_expiration_error.rb +0 -18
- data/yesql.gemspec +0 -27
@@ -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
|
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
|
data/lib/yesql/bindings/utils.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module YeSQL
|
3
|
+
module ::YeSQL
|
4
4
|
module Bindings
|
5
5
|
module Utils
|
6
|
+
include ::YeSQL::Utils::Read
|
7
|
+
|
6
8
|
def statement_binds(extractor)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
statement(readable: true)
|
10
|
+
.scan(::YeSQL::BIND_REGEX)
|
11
|
+
.map(&:first)
|
12
|
+
.map { extractor[_1.to_sym][:bind].values_at(:vals, :vars) }
|
11
13
|
end
|
12
14
|
end
|
13
15
|
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"
|
11
|
+
def pg? = adapter == "PostgreSQL"
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def adapter = ::ActiveRecord::Base.connection.adapter_name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -3,20 +3,19 @@
|
|
3
3
|
module ::YeSQL
|
4
4
|
module Config
|
5
5
|
class Configuration
|
6
|
-
attr_accessor :path
|
6
|
+
attr_accessor :connection, :path
|
7
7
|
|
8
|
-
DEFAULT_PATH =
|
8
|
+
DEFAULT_PATH = "app/yesql"
|
9
9
|
|
10
10
|
def initialize
|
11
|
+
@connection = ""
|
11
12
|
@path = DEFAULT_PATH
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
17
|
class << self
|
17
|
-
def config
|
18
|
-
@config ||= ::YeSQL::Configuration.new
|
19
|
-
end
|
18
|
+
def config = @config ||= ::YeSQL::Configuration.new
|
20
19
|
|
21
20
|
def configure
|
22
21
|
yield config if block_given?
|
@@ -1,17 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module YeSQL
|
3
|
+
module ::YeSQL
|
4
4
|
module Errors
|
5
|
-
|
6
|
-
def
|
7
|
-
|
5
|
+
class FilePathDoesNotExistError
|
6
|
+
def initialize(file_path)
|
7
|
+
@file_path = file_path
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
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
|
+
)
|
11
22
|
end
|
12
23
|
|
13
24
|
private
|
14
25
|
|
26
|
+
attr_reader :file_path
|
27
|
+
|
15
28
|
MESSAGE = <<~MSG
|
16
29
|
|
17
30
|
SQL file "%<file_path>s" does not exist in %<path>s.
|
@@ -22,17 +35,9 @@ module YeSQL
|
|
22
35
|
MSG
|
23
36
|
private_constant :MESSAGE
|
24
37
|
|
25
|
-
def file_exists?(file_path)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def available_files
|
30
|
-
path_files.map { |file| "- #{file}\n" }.join
|
31
|
-
end
|
32
|
-
|
33
|
-
def path_files
|
34
|
-
@path_files ||= Dir["#{::YeSQL.config.path}/**/*.sql"]
|
35
|
-
end
|
38
|
+
def file_exists? = path_files.any? { _1.include?("#{file_path}.sql") }
|
39
|
+
def path_files = @path_files ||= Dir["#{::YeSQL.config.path}/**/*.sql"]
|
40
|
+
def available_files = path_files.map { "- #{_1}\n" }.join
|
36
41
|
end
|
37
42
|
end
|
38
43
|
end
|
@@ -1,18 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require "yesql/utils/read"
|
4
|
+
|
5
|
+
module ::YeSQL
|
4
6
|
module Errors
|
5
|
-
|
6
|
-
|
7
|
-
return unless statement_binds(file_path).size.positive?
|
7
|
+
class NoBindingsProvidedError
|
8
|
+
include ::YeSQL::Utils::Read
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
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)
|
12
20
|
end
|
13
21
|
|
14
22
|
private
|
15
23
|
|
24
|
+
attr_reader :binds, :file_path
|
25
|
+
|
16
26
|
MESSAGE = <<~MSG
|
17
27
|
|
18
28
|
YeSQL invoked without bindings.
|
@@ -23,18 +33,9 @@ module YeSQL
|
|
23
33
|
MSG
|
24
34
|
private_constant :MESSAGE
|
25
35
|
|
26
|
-
def statement_binds(
|
27
|
-
|
28
|
-
|
29
|
-
break [] if scanned_binds.size.zero?
|
30
|
-
|
31
|
-
break scanned_binds.sort
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def renderable_statement_binds(file_path)
|
36
|
-
statement_binds(file_path).flatten.map { |bind| "- `#{bind}`\n" }.join
|
37
|
-
end
|
36
|
+
def statement_binds = statement.scan(::YeSQL::BIND_REGEX).tap { break (_1 || []).sort }
|
37
|
+
def renderable_statement_binds = statement_binds.flatten.map { "- `#{_1}`\n" }.join
|
38
|
+
def expected_binds? = binds.is_a?(::Hash) && !binds.empty?
|
38
39
|
end
|
39
40
|
end
|
40
41
|
end
|
@@ -1,14 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module YeSQL
|
3
|
+
module ::YeSQL
|
4
4
|
module Errors
|
5
|
-
|
6
|
-
def
|
5
|
+
class OutputArgumentError
|
6
|
+
def initialize(output)
|
7
|
+
@output = output
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate_output_options
|
7
11
|
return if output.nil?
|
12
|
+
return if OPTIONS.include?(output.to_sym)
|
8
13
|
|
9
|
-
raise ArgumentError, format(MESSAGE, output)
|
14
|
+
raise ArgumentError, format(MESSAGE, output)
|
10
15
|
end
|
11
16
|
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :output
|
20
|
+
|
12
21
|
MESSAGE = <<~MSG
|
13
22
|
Unsupported `output` option given `%s`. Possible values are:
|
14
23
|
- `columns`: returns an array with the columns from the result.
|
@@ -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"
|
16
|
+
def rows? = output == "rows"
|
17
|
+
def hash? = output == "hash"
|
18
|
+
|
19
|
+
def_delegator(:output, :to_sym)
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :output
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,86 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
|
7
|
-
|
3
|
+
require "yesql"
|
4
|
+
require "yesql/bindings/utils"
|
5
|
+
require "yesql/common/adapter"
|
6
|
+
require "yesql/bindings/transformed"
|
7
|
+
require "yesql/query/result"
|
8
|
+
require "yesql/query/transform_result"
|
9
|
+
require "yesql/params/output"
|
10
|
+
|
11
|
+
module ::YeSQL
|
8
12
|
module Query
|
9
13
|
class Performer
|
10
|
-
extend Forwardable
|
11
|
-
|
12
14
|
include ::YeSQL::Bindings::Utils
|
13
15
|
|
14
|
-
|
15
|
-
def initialize(bind_statement:,
|
16
|
-
bindings: {},
|
17
|
-
cache: {},
|
18
|
-
file_path:,
|
19
|
-
output: :rows,
|
20
|
-
prepare: false)
|
16
|
+
def initialize(bind_statement:, file_path:, bindings: {}, output: :rows, prepare: false)
|
21
17
|
@bind_statement = bind_statement
|
22
|
-
@cache = cache
|
23
|
-
@cache_key = cache[:key] || file_path
|
24
|
-
@connection = ActiveRecord::Base.connection
|
25
|
-
@expires_in = cache[:expires_in]
|
26
18
|
@file_path = file_path
|
27
19
|
@named_bindings = bindings.transform_keys(&:to_sym)
|
28
|
-
@output = output
|
20
|
+
@output = ::YeSQL::Params::Output.new(output)
|
29
21
|
@prepare = prepare
|
30
22
|
end
|
31
|
-
# rubocop:enable Metrics/ParameterLists
|
32
|
-
|
33
|
-
def call
|
34
|
-
return modified_output if cache.empty?
|
35
23
|
|
36
|
-
|
37
|
-
end
|
24
|
+
def call = ::YeSQL::Query::TransformResult.new(output: output, result: query_result).call
|
38
25
|
|
39
26
|
private
|
40
27
|
|
41
|
-
attr_reader :bind_statement,
|
42
|
-
:cache,
|
43
|
-
:cache_key,
|
44
|
-
:connection,
|
45
|
-
:expires_in,
|
46
|
-
:file_path,
|
47
|
-
:named_bindings,
|
48
|
-
:output,
|
49
|
-
:prepare,
|
50
|
-
:rows
|
51
|
-
|
52
|
-
def_delegator(:query_result, :columns)
|
53
|
-
private :columns
|
54
|
-
def_delegator(:query_result, :rows)
|
55
|
-
private :rows
|
56
|
-
|
57
|
-
def modified_output
|
58
|
-
@modified_output ||=
|
59
|
-
begin
|
60
|
-
return query_result.public_send(output) if %w[columns rows].include?(output.to_s)
|
61
|
-
|
62
|
-
columns.map(&:to_sym).tap { |cols| break rows.map { |row| cols.zip(row).to_h } }
|
63
|
-
end
|
64
|
-
end
|
28
|
+
attr_reader :bind_statement, :file_path, :named_bindings, :output, :prepare
|
65
29
|
|
66
30
|
def query_result
|
67
|
-
@query_result ||=
|
31
|
+
@query_result ||= ::YeSQL::Query::Result.new(binds: binds,
|
32
|
+
bind_statement: bind_statement,
|
33
|
+
file_path: file_path,
|
34
|
+
prepare: prepare).call
|
68
35
|
end
|
69
36
|
|
70
|
-
def extractor
|
71
|
-
::YeSQL::Bindings::Extractor.new(bindings: named_bindings).call
|
72
|
-
end
|
73
|
-
|
74
|
-
# TODO: move this somewhere else.
|
75
37
|
def binds
|
76
|
-
statement_binds(extractor)
|
77
|
-
.sort_by { |_, position| position.tr('$', '').to_i }
|
78
|
-
.uniq
|
79
|
-
.map(&:first)
|
80
|
-
.flatten
|
81
|
-
.each_slice(2)
|
82
|
-
.to_a
|
38
|
+
::YeSQL::Bindings::Transformed.new(statement_binds: statement_binds(extractor)).call
|
83
39
|
end
|
40
|
+
|
41
|
+
def extractor = ::YeSQL::Bindings::Extractor.new(bindings: named_bindings).call
|
84
42
|
end
|
85
43
|
end
|
86
44
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yesql"
|
4
|
+
require "forwardable"
|
5
|
+
require "yesql/common/adapter"
|
6
|
+
|
7
|
+
module ::YeSQL
|
8
|
+
module Query
|
9
|
+
class Result
|
10
|
+
extend ::Forwardable
|
11
|
+
|
12
|
+
include ::YeSQL::Common::Adapter
|
13
|
+
|
14
|
+
def initialize(bind_statement:, file_path:, prepare:, binds: [])
|
15
|
+
@binds = binds
|
16
|
+
@bind_statement = bind_statement
|
17
|
+
@file_path = file_path
|
18
|
+
@prepare_option = prepare
|
19
|
+
end
|
20
|
+
|
21
|
+
def call
|
22
|
+
return view_result if view?
|
23
|
+
return rails5_result if ::ActiveRecord::VERSION::MAJOR == 5 && mysql?
|
24
|
+
|
25
|
+
exec_query(bound, file_path, binds, prepare: prepare_option)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :binds, :bind_statement, :file_path, :prepare_option
|
31
|
+
|
32
|
+
def_delegators(:bind_statement, :bound, :to_s, :view?)
|
33
|
+
def_delegators(:connection, :exec_query, :raw_connection)
|
34
|
+
def_delegators(:raw_connection, :prepare)
|
35
|
+
|
36
|
+
def view_result = exec_query(bound)
|
37
|
+
def rails5_result = prepare(bound).execute(*binds)
|
38
|
+
def connection = @connection ||= ActiveRecord::Base.connection
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|