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.
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 +25 -31
  29. data/lib/yesql/bindings/extract.rb +15 -21
  30. data/lib/yesql/bindings/extractor.rb +21 -21
  31. data/lib/yesql/bindings/transformed.rb +8 -11
  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 +18 -19
  37. data/lib/yesql/errors/output_argument_error.rb +13 -4
  38. data/lib/yesql/params/output.rb +6 -14
  39. data/lib/yesql/query/performer.rb +13 -42
  40. data/lib/yesql/query/result.rb +12 -17
  41. data/lib/yesql/query/transform_result.rb +11 -21
  42. data/lib/yesql/statement.rb +19 -24
  43. data/lib/yesql/utils/read.rb +11 -8
  44. data/lib/yesql/version.rb +1 -1
  45. metadata +47 -17
  46. data/.gitignore +0 -4
  47. data/.rubocop.yml +0 -20
  48. data/Gemfile +0 -9
  49. data/Gemfile.lock +0 -167
  50. data/Rakefile +0 -1
  51. data/bin/console +0 -14
  52. data/bin/setup +0 -8
  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,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql/statement'
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
-
12
- 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
13
16
  include ::YeSQL::Config
14
- include ::YeSQL::Errors::CacheExpirationError
15
- include ::YeSQL::Errors::FilePathDoesNotExistError
16
- include ::YeSQL::Errors::NoBindingsProvidedError
17
- include ::YeSQL::Errors::OutputArgumentError
18
17
 
19
- BIND_REGEX = /(?<!:):(\w+)(?=\b)/.freeze
18
+ BIND_REGEX = /(?<!:):(\w+)(?=\b)/
20
19
 
21
- # rubocop:disable Naming/MethodName
22
- def YeSQL(file_path, bindings = {}, options = {})
20
+ def YeSQL(file_path, binds = {}, options = {})
23
21
  output = options[:output] || :rows
24
- cache = options[:cache] || {}
25
22
 
26
- validate(bindings, cache, file_path, output)
27
- execute(bindings, cache, file_path, output, options)
23
+ validate(binds, file_path, output)
24
+ execute(binds, file_path, output, options[:prepare])
28
25
  end
29
- # rubocop:enable Naming/MethodName
30
26
 
31
27
  private
32
28
 
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?
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
38
33
  end
39
34
 
40
- def execute(bindings, cache, file_path, output, options)
35
+ def execute(binds, file_path, output, prepare)
41
36
  ::YeSQL::Query::Performer.new(
42
- bindings: bindings,
43
- bind_statement: ::YeSQL::Statement.new(bindings, file_path),
44
- cache: cache,
37
+ bindings: binds,
38
+ bind_statement: ::YeSQL::Statement.new(binds, file_path),
45
39
  file_path: file_path,
46
40
  output: output,
47
- prepare: options[:prepare]
41
+ prepare: prepare
48
42
  ).call
49
43
  end
50
44
  end
@@ -1,54 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql'
4
- require 'yesql/common/adapter'
3
+ require "yesql"
4
+ require "yesql/common/adapter"
5
5
 
6
6
  module ::YeSQL
7
7
  module Bindings
8
8
  class Extract
9
9
  include ::YeSQL::Common::Adapter
10
10
 
11
- def initialize(indexed_bindings, hash, index, value)
12
- @indexed_bindings = indexed_bindings
11
+ def initialize(hash, indexed_bindings, index, value)
13
12
  @hash = hash
14
13
  @index = index
14
+ @indexed_bindings = indexed_bindings
15
15
  @value = value
16
16
  end
17
17
 
18
18
  def bind_vals
19
19
  return [nil, value] unless array?
20
20
 
21
- value.map { |bind| [nil, bind] }
21
+ value.map { [nil, _1] }
22
22
  end
23
23
 
24
24
  def bind_vars
25
25
  if mysql?
26
- return '?' unless array?
26
+ return "?" unless array?
27
27
 
28
- Array.new(value.size, '?').join(', ')
28
+ Array.new(size, "?").join(", ")
29
29
  elsif pg?
30
30
  return "$#{last_val}" unless array?
31
31
 
32
- value.map.with_index(bind_index) { |_, i| "$#{i}" }.join(', ')
32
+ value.map.with_index(bind_index) { "$#{_2}" }.join(", ")
33
33
  end
34
34
  end
35
35
 
36
- def last_val
37
- prev_last_val + current_val_size
38
- end
39
-
40
36
  def prev
41
37
  return if first?
42
38
 
43
39
  indexed_bindings[index - 2].first
44
40
  end
45
41
 
42
+ def last_val = prev_last_val + current_val_size
43
+
46
44
  private
47
45
 
48
46
  attr_reader :hash, :index, :indexed_bindings, :value
49
47
 
50
48
  def current_val_size
51
- return value.size if array?
49
+ return size if array?
52
50
 
53
51
  1
54
52
  end
@@ -59,19 +57,15 @@ module ::YeSQL
59
57
  hash[prev][:last_val]
60
58
  end
61
59
 
62
- def first?
63
- index == 1
64
- end
65
-
66
- def array?
67
- value.is_a?(Array)
68
- end
69
-
70
60
  def bind_index
71
61
  return 1 if first?
72
62
 
73
63
  prev_last_val + 1
74
64
  end
65
+
66
+ def size = value.size
67
+ def first? = index == 1
68
+ def array? = value.is_a?(Array)
75
69
  end
76
70
  end
77
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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql'
4
- require 'yesql/common/adapter'
3
+ require "yesql"
4
+ require "yesql/common/adapter"
5
5
 
6
6
  module ::YeSQL
7
7
  module Bindings
@@ -23,19 +23,16 @@ module ::YeSQL
23
23
 
24
24
  attr_reader :statement_binds
25
25
 
26
- def rails5?
27
- ::Rails::VERSION::MAJOR == 5
28
- end
26
+ def rails5? = ::ActiveRecord::VERSION::MAJOR == 5
29
27
 
30
28
  def mysql_rails5_binds
31
29
  statement_binds
32
- .map(&:first)
33
- .flatten(1)
30
+ .flat_map(&:first)
34
31
  .each_slice(2)
35
- .flat_map do |first, last|
36
- next [first, last].compact.map(&:last) if first.is_a?(Array)
32
+ .flat_map do
33
+ next [_1, _2].compact.map(&:last) if _1.is_a?(Array)
37
34
 
38
- last
35
+ _2
39
36
  end
40
37
  end
41
38
 
@@ -49,7 +46,7 @@ module ::YeSQL
49
46
 
50
47
  def pg_binds
51
48
  statement_binds
52
- .sort_by { |_, position| position.to_s.tr('$', '').to_i }
49
+ .sort_by { _2.to_s.tr("$", "").to_i }
53
50
  .uniq
54
51
  .map(&:first)
55
52
  .flatten