yesql 0.1.5 → 0.2.1

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -22
  3. data/lib/.rbnext/2.3/yesql/errors/file_path_does_not_exist_error.rb +40 -0
  4. data/lib/.rbnext/2.3/yesql/errors/no_bindings_provided_error.rb +38 -0
  5. data/lib/.rbnext/2.3/yesql/errors/output_argument_error.rb +31 -0
  6. data/lib/.rbnext/2.3/yesql/query/transform_result.rb +48 -0
  7. data/lib/.rbnext/2.7/yesql/bindings/extract.rb +71 -0
  8. data/lib/.rbnext/2.7/yesql/bindings/extractor.rb +38 -0
  9. data/lib/.rbnext/2.7/yesql/bindings/transformed.rb +58 -0
  10. data/lib/.rbnext/2.7/yesql/bindings/utils.rb +16 -0
  11. data/lib/.rbnext/2.7/yesql/errors/file_path_does_not_exist_error.rb +43 -0
  12. data/lib/.rbnext/2.7/yesql/errors/no_bindings_provided_error.rb +41 -0
  13. data/lib/.rbnext/2.7/yesql/query/transform_result.rb +48 -0
  14. data/lib/.rbnext/2.7/yesql/statement.rb +44 -0
  15. data/lib/.rbnext/2.7/yesql/utils/read.rb +20 -0
  16. data/lib/.rbnext/3.0/yesql/bindings/extract.rb +71 -0
  17. data/lib/.rbnext/3.0/yesql/bindings/transformed.rb +58 -0
  18. data/lib/.rbnext/3.0/yesql/common/adapter.rb +18 -0
  19. data/lib/.rbnext/3.0/yesql/config/configuration.rb +32 -0
  20. data/lib/.rbnext/3.0/yesql/errors/file_path_does_not_exist_error.rb +43 -0
  21. data/lib/.rbnext/3.0/yesql/errors/no_bindings_provided_error.rb +41 -0
  22. data/lib/.rbnext/3.0/yesql/params/output.rb +26 -0
  23. data/lib/.rbnext/3.0/yesql/query/performer.rb +44 -0
  24. data/lib/.rbnext/3.0/yesql/query/result.rb +41 -0
  25. data/lib/.rbnext/3.0/yesql/query/transform_result.rb +48 -0
  26. data/lib/.rbnext/3.0/yesql/statement.rb +44 -0
  27. data/lib/.rbnext/3.0/yesql/utils/read.rb +20 -0
  28. data/lib/yesql.rb +26 -31
  29. data/lib/yesql/bindings/extract.rb +15 -20
  30. data/lib/yesql/bindings/extractor.rb +21 -21
  31. data/lib/yesql/bindings/transformed.rb +58 -0
  32. data/lib/yesql/bindings/utils.rb +7 -5
  33. data/lib/yesql/common/adapter.rb +6 -13
  34. data/lib/yesql/config/configuration.rb +4 -5
  35. data/lib/yesql/errors/file_path_does_not_exist_error.rb +22 -17
  36. data/lib/yesql/errors/no_bindings_provided_error.rb +20 -19
  37. data/lib/yesql/errors/output_argument_error.rb +13 -4
  38. data/lib/yesql/params/output.rb +26 -0
  39. data/lib/yesql/query/performer.rb +20 -64
  40. data/lib/yesql/query/result.rb +41 -0
  41. data/lib/yesql/query/transform_result.rb +48 -0
  42. data/lib/yesql/statement.rb +44 -0
  43. data/lib/yesql/utils/read.rb +11 -8
  44. data/lib/yesql/version.rb +1 -1
  45. metadata +67 -18
  46. data/.gitignore +0 -1
  47. data/Gemfile +0 -9
  48. data/Gemfile.lock +0 -167
  49. data/Rakefile +0 -1
  50. data/bin/console +0 -14
  51. data/bin/setup +0 -8
  52. data/lib/yesql/bindings/binder.rb +0 -19
  53. data/lib/yesql/errors/cache_expiration_error.rb +0 -18
  54. data/yesql.gemspec +0 -28
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
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
12
+ module Query
13
+ class Performer
14
+ include ::YeSQL::Bindings::Utils
15
+
16
+ def initialize(bind_statement:, file_path:, bindings: {}, output: :rows, prepare: false)
17
+ @bind_statement = bind_statement
18
+ @file_path = file_path
19
+ @named_bindings = bindings.transform_keys(&:to_sym)
20
+ @output = ::YeSQL::Params::Output.new(output)
21
+ @prepare = prepare
22
+ end
23
+
24
+ def call ; ::YeSQL::Query::TransformResult.new(output: output, result: query_result).call; end
25
+
26
+ private
27
+
28
+ attr_reader :bind_statement, :file_path, :named_bindings, :output, :prepare
29
+
30
+ def 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
35
+ end
36
+
37
+ def binds
38
+ ::YeSQL::Bindings::Transformed.new(statement_binds: statement_binds(extractor)).call
39
+ end
40
+
41
+ def extractor ; ::YeSQL::Bindings::Extractor.new(bindings: named_bindings).call; end
42
+ end
43
+ end
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); end
37
+ def rails5_result ; prepare(bound).execute(*binds); end
38
+ def connection ; @connection ||= ActiveRecord::Base.connection; 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&.symbolize_keys || _1 }
40
+ end
41
+
42
+ def columns ; fields || result.columns; end
43
+ def rows_values ; to_a.map { _1.respond_to?(:values) ? _1.values : _1 }; end
44
+ def hashed_rows(rows) ; rows.map { 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) { 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.include?("#{file_path}.sql") }; end
17
+ def dir_sql_files ; Dir["./#{::YeSQL.config.path}/**/*.sql"]; end
18
+ end
19
+ end
20
+ end
data/lib/yesql.rb CHANGED
@@ -1,50 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql/version'
4
- require 'yesql/config/configuration'
5
- require 'yesql/query/performer'
6
- require 'yesql/errors/cache_expiration_error'
7
- require 'yesql/errors/file_path_does_not_exist_error'
8
- require 'yesql/errors/no_bindings_provided_error'
9
- require 'yesql/errors/output_argument_error'
10
- require 'yesql/bindings/binder'
11
-
12
- module YeSQL
3
+ require "backports/2.0"
4
+ require "ruby-next/language/setup"
5
+
6
+ RubyNext::Language.setup_gem_load_path
7
+
8
+ require "yesql/statement"
9
+ require "yesql/version"
10
+ require "yesql/config/configuration"
11
+ require "yesql/query/performer"
12
+ require "yesql/errors/file_path_does_not_exist_error"
13
+ require "yesql/errors/no_bindings_provided_error"
14
+ require "yesql/errors/output_argument_error"
15
+
16
+ module ::YeSQL
13
17
  include ::YeSQL::Config
14
- include ::YeSQL::Errors::CacheExpirationError
15
- include ::YeSQL::Errors::FilePathDoesNotExistError
16
- include ::YeSQL::Errors::NoBindingsProvidedError
17
- include ::YeSQL::Errors::OutputArgumentError
18
18
 
19
- BIND_REGEX = /(?<!:):(\w+)(?=\b)/.freeze
19
+ BIND_REGEX = /(?<!:):(\w+)(?=\b)/
20
20
 
21
- # rubocop:disable Naming/MethodName
22
- def YeSQL(file_path, bindings = {}, options = {})
21
+ def YeSQL(file_path, binds = {}, options = {})
23
22
  output = options[:output] || :rows
24
- cache = options[:cache] || {}
25
23
 
26
- validate(bindings, cache, file_path, output)
27
- execute(bindings, cache, file_path, output, options)
24
+ validate(binds, file_path, output)
25
+ execute(binds, file_path, output, options[:prepare])
28
26
  end
29
- # rubocop:enable Naming/MethodName
30
27
 
31
28
  private
32
29
 
33
- def validate(bindings, cache, file_path, output)
34
- validate_file_path_existence(file_path)
35
- validate_statement_bindings(bindings, file_path)
36
- validate_output_options(output)
37
- validate_cache_expiration(cache[:expires_in]) unless cache.empty?
30
+ def validate(binds, file_path, output)
31
+ ::YeSQL::Errors::FilePathDoesNotExistError.new(file_path).validate_file_path_existence
32
+ ::YeSQL::Errors::NoBindingsProvidedError.new(binds, file_path).validate_statement_bindings
33
+ ::YeSQL::Errors::OutputArgumentError.new(output).validate_output_options
38
34
  end
39
35
 
40
- def execute(bindings, cache, file_path, output, options)
36
+ def execute(binds, file_path, output, prepare)
41
37
  ::YeSQL::Query::Performer.new(
42
- bindings: bindings,
43
- bind_statement: ::YeSQL::Bindings::Binder.bind_statement(file_path, bindings),
44
- cache: cache,
38
+ bindings: binds,
39
+ bind_statement: ::YeSQL::Statement.new(binds, file_path),
45
40
  file_path: file_path,
46
41
  output: output,
47
- prepare: options[:prepare]
42
+ prepare: prepare
48
43
  ).call
49
44
  end
50
45
  end
@@ -1,53 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql'
3
+ require "yesql"
4
+ require "yesql/common/adapter"
4
5
 
5
6
  module ::YeSQL
6
7
  module Bindings
7
8
  class Extract
8
9
  include ::YeSQL::Common::Adapter
9
10
 
10
- def initialize(indexed_bindings, hash, index, value)
11
- @indexed_bindings = indexed_bindings
11
+ def initialize(hash, indexed_bindings, index, value)
12
12
  @hash = hash
13
13
  @index = index
14
+ @indexed_bindings = indexed_bindings
14
15
  @value = value
15
16
  end
16
17
 
17
18
  def bind_vals
18
19
  return [nil, value] unless array?
19
20
 
20
- value.map { |bind| [nil, bind] }
21
+ value.map { [nil, _1] }
21
22
  end
22
23
 
23
24
  def bind_vars
24
25
  if mysql?
25
- return '?' unless array?
26
+ return "?" unless array?
26
27
 
27
- Array.new(value.size, '?').join(', ')
28
+ Array.new(size, "?").join(", ")
28
29
  elsif pg?
29
30
  return "$#{last_val}" unless array?
30
31
 
31
- value.map.with_index(bind_index) { |_, i| "$#{i}" }.join(', ')
32
+ value.map.with_index(bind_index) { "$#{_2}" }.join(", ")
32
33
  end
33
34
  end
34
35
 
35
- def last_val
36
- prev_last_val + current_val_size
37
- end
38
-
39
36
  def prev
40
37
  return if first?
41
38
 
42
39
  indexed_bindings[index - 2].first
43
40
  end
44
41
 
42
+ def last_val = prev_last_val + current_val_size
43
+
45
44
  private
46
45
 
47
46
  attr_reader :hash, :index, :indexed_bindings, :value
48
47
 
49
48
  def current_val_size
50
- return value.size if array?
49
+ return size if array?
51
50
 
52
51
  1
53
52
  end
@@ -58,19 +57,15 @@ module ::YeSQL
58
57
  hash[prev][:last_val]
59
58
  end
60
59
 
61
- def first?
62
- index == 1
63
- end
64
-
65
- def array?
66
- value.is_a?(Array)
67
- end
68
-
69
60
  def bind_index
70
61
  return 1 if first?
71
62
 
72
63
  prev_last_val + 1
73
64
  end
65
+
66
+ def size = value.size
67
+ def first? = index == 1
68
+ def array? = value.is_a?(Array)
74
69
  end
75
70
  end
76
71
  end
@@ -1,38 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql/bindings/extract'
3
+ require "yesql/bindings/extract"
4
4
 
5
- module YeSQL
5
+ module ::YeSQL
6
6
  module Bindings
7
7
  class Extractor
8
- def initialize(bindings:)
8
+ def initialize(bindings: {})
9
9
  @bindings = bindings
10
- @indexed_bindings = (bindings || {}).to_a
10
+ @indexed_bindings = bindings.to_a
11
11
  end
12
12
 
13
- # rubocop:disable Metrics/MethodLength
14
13
  def call
15
- bindings.each_with_object({}).with_index(1) do |((key, value), hash), index|
16
- hash[key] =
17
- ::YeSQL::Bindings::Extract.new(indexed_bindings, hash, index, value).tap do |extract|
18
- break {
19
- bind: {
20
- vals: extract.bind_vals,
21
- vars: extract.bind_vars
22
- },
23
- last_val: extract.last_val,
24
- match: key,
25
- prev: extract.prev,
26
- value: value
27
- }
28
- end
29
- end
14
+ bindings
15
+ .each_with_object({})
16
+ .with_index(1) do |((key, value), hash), index|
17
+ hash[key] = binding_extracts(hash, indexed_bindings, index, key, value)
18
+ end
30
19
  end
31
- # rubocop:enable Metrics/MethodLength
32
20
 
33
21
  private
34
22
 
35
23
  attr_reader :bindings, :indexed_bindings
24
+
25
+ def binding_extracts(hash, indexed_bindings, index, key, value)
26
+ ::YeSQL::Bindings::Extract.new(hash, indexed_bindings, index, value).tap do
27
+ break {
28
+ bind: { vals: _1.bind_vals, vars: _1.bind_vars },
29
+ last_val: _1.last_val,
30
+ match: key,
31
+ prev: _1.prev,
32
+ value: value
33
+ }
34
+ end
35
+ end
36
36
  end
37
37
  end
38
38
  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
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