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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -31
  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 +25 -32
  29. data/lib/yesql/bindings/extract.rb +22 -19
  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 +18 -0
  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 -62
  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 +68 -18
  46. data/.gitignore +0 -1
  47. data/Gemfile +0 -9
  48. data/Gemfile.lock +0 -165
  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 -27
@@ -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
@@ -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,51 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pry'
4
- require 'yesql/version'
5
- require 'yesql/config/configuration'
6
- require 'yesql/query/performer'
7
- require 'yesql/errors/cache_expiration_error'
8
- require 'yesql/errors/file_path_does_not_exist_error'
9
- require 'yesql/errors/no_bindings_provided_error'
10
- require 'yesql/errors/output_argument_error'
11
- require 'yesql/bindings/binder'
12
-
13
- module YeSQL
3
+ require "ruby-next/language/setup"
4
+
5
+ RubyNext::Language.setup_gem_load_path
6
+
7
+ require "yesql/statement"
8
+ require "yesql/version"
9
+ require "yesql/config/configuration"
10
+ require "yesql/query/performer"
11
+ require "yesql/errors/file_path_does_not_exist_error"
12
+ require "yesql/errors/no_bindings_provided_error"
13
+ require "yesql/errors/output_argument_error"
14
+
15
+ module ::YeSQL
14
16
  include ::YeSQL::Config
15
- include ::YeSQL::Errors::CacheExpirationError
16
- include ::YeSQL::Errors::FilePathDoesNotExistError
17
- include ::YeSQL::Errors::NoBindingsProvidedError
18
- include ::YeSQL::Errors::OutputArgumentError
19
17
 
20
- BIND_REGEX = /(?<!:):(\w+)(?=\b)/.freeze
18
+ BIND_REGEX = /(?<!:):(\w+)(?=\b)/
21
19
 
22
- # rubocop:disable Naming/MethodName
23
- def YeSQL(file_path, bindings = {}, options = {})
20
+ def YeSQL(file_path, binds = {}, options = {})
24
21
  output = options[:output] || :rows
25
- cache = options[:cache] || {}
26
22
 
27
- validate(bindings, cache, file_path, output)
28
- execute(bindings, cache, file_path, output, options)
23
+ validate(binds, file_path, output)
24
+ execute(binds, file_path, output, options[:prepare])
29
25
  end
30
- # rubocop:enable Naming/MethodName
31
26
 
32
27
  private
33
28
 
34
- def validate(bindings, cache, file_path, output)
35
- validate_file_path_existence(file_path)
36
- validate_statement_bindings(bindings, file_path)
37
- validate_output_options(output)
38
- validate_cache_expiration(cache[:expires_in]) unless cache.empty?
29
+ def validate(binds, file_path, output)
30
+ ::YeSQL::Errors::FilePathDoesNotExistError.new(file_path).validate_file_path_existence
31
+ ::YeSQL::Errors::NoBindingsProvidedError.new(binds, file_path).validate_statement_bindings
32
+ ::YeSQL::Errors::OutputArgumentError.new(output).validate_output_options
39
33
  end
40
34
 
41
- def execute(bindings, cache, file_path, output, options)
35
+ def execute(binds, file_path, output, prepare)
42
36
  ::YeSQL::Query::Performer.new(
43
- bindings: bindings,
44
- bind_statement: ::YeSQL::Bindings::Binder.bind_statement(file_path, bindings),
45
- cache: cache,
37
+ bindings: binds,
38
+ bind_statement: ::YeSQL::Statement.new(binds, file_path),
46
39
  file_path: file_path,
47
40
  output: output,
48
- prepare: options[:prepare]
41
+ prepare: prepare
49
42
  ).call
50
43
  end
51
44
  end
@@ -1,31 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql'
3
+ require "yesql"
4
+ require "yesql/common/adapter"
4
5
 
5
- module YeSQL
6
+ module ::YeSQL
6
7
  module Bindings
7
8
  class Extract
8
- def initialize(indexed_bindings, hash, index, value)
9
- @indexed_bindings = indexed_bindings
9
+ include ::YeSQL::Common::Adapter
10
+
11
+ def initialize(hash, indexed_bindings, index, value)
10
12
  @hash = hash
11
13
  @index = index
14
+ @indexed_bindings = indexed_bindings
12
15
  @value = value
13
16
  end
14
17
 
15
18
  def bind_vals
16
19
  return [nil, value] unless array?
17
20
 
18
- value.map { |bind| [nil, bind] }
21
+ value.map { [nil, _1] }
19
22
  end
20
23
 
21
24
  def bind_vars
22
- return "$#{last_val}" unless array?
25
+ if mysql?
26
+ return "?" unless array?
23
27
 
24
- value.map.with_index(bind_index) { |_, i| "$#{i}" }.join(', ')
25
- end
28
+ Array.new(size, "?").join(", ")
29
+ elsif pg?
30
+ return "$#{last_val}" unless array?
26
31
 
27
- def last_val
28
- prev_last_val + current_val_size
32
+ value.map.with_index(bind_index) { "$#{_2}" }.join(", ")
33
+ end
29
34
  end
30
35
 
31
36
  def prev
@@ -34,12 +39,14 @@ module YeSQL
34
39
  indexed_bindings[index - 2].first
35
40
  end
36
41
 
42
+ def last_val = prev_last_val + current_val_size
43
+
37
44
  private
38
45
 
39
46
  attr_reader :hash, :index, :indexed_bindings, :value
40
47
 
41
48
  def current_val_size
42
- return value.size if array?
49
+ return size if array?
43
50
 
44
51
  1
45
52
  end
@@ -50,19 +57,15 @@ module YeSQL
50
57
  hash[prev][:last_val]
51
58
  end
52
59
 
53
- def first?
54
- index == 1
55
- end
56
-
57
- def array?
58
- value.is_a?(Array)
59
- end
60
-
61
60
  def bind_index
62
61
  return 1 if first?
63
62
 
64
63
  prev_last_val + 1
65
64
  end
65
+
66
+ def size = value.size
67
+ def first? = index == 1
68
+ def array? = value.is_a?(Array)
66
69
  end
67
70
  end
68
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