yesql 0.1.3 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f34614332a4d7180b1ef1eee59ee5745706bd7c4f6a8aae1ad1dd3ad729c75e
4
- data.tar.gz: 8600ffe5021418c3d2ac822d78744cedcc9d6116da7c9168fde36c711e783582
3
+ metadata.gz: 52fad054eefb567edd4b4516c9020170f69adfc7f3e9bd5bc3fbb48c529c4ee8
4
+ data.tar.gz: 4aba16fea0544f89a2b431eea2af65946e99675065692cbb20f4ffde1e2f6cf1
5
5
  SHA512:
6
- metadata.gz: f9c9cb626349ef40ec0ad7ba2c13b034d6844def674ec86d52047cedf53a6241f2022c070f0dc7f329a8c89329553754f88870e8ee61493a6a3708e0f5328e9f
7
- data.tar.gz: 997cb3883fabfcb39ecd36d80ae9df990a331bbc8254edac4618bbc1f58e2b9c9971430a9f8d2bd29f3a30ac2a86d7700a0eedd1a2c5a840f406644824aef954
6
+ metadata.gz: a5c739cb79f30e6e9165de184e34f23394959933792aac33ada3829438cde7ad21639d257a3e5e78fd5ae3badf7eb5652ffaa5ee6348f4c4e349acd9140682f2
7
+ data.tar.gz: 32673b4339aa6b0f3a71b61ec3ab7fe726f9df58f83c2c68fe142cf2b3a267d4369853e6011582d334723d222e9d59caa99916c64cb09e364e5991a5700d1a1f
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ spec/minimalpg/log/*
2
+ spec/minimalmysql/log/*
3
+ spec/minimalpg/db/*
4
+ spec/minimalmysql/db/*
data/.rubocop.yml ADDED
@@ -0,0 +1,20 @@
1
+ require: rubocop-rspec
2
+
3
+ Metrics/BlockLength:
4
+ Exclude:
5
+ - spec/**/*_spec.rb
6
+ - spec/spec_helper.rb
7
+ - spec/yesql/shared/*
8
+
9
+ RSpec/FilePath:
10
+ Exclude:
11
+ - spec/**/*_spec.rb
12
+
13
+ RSpec/NestedGroups:
14
+ Enabled: False
15
+
16
+ RSpec/ExampleLength:
17
+ Enabled: False
18
+
19
+ RSpec/BeforeAfterAll:
20
+ Enabled: False
data/Gemfile.lock CHANGED
@@ -1,63 +1,62 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- yesql (0.1.0)
5
- dry-configurable (~> 0.11.6)
4
+ yesql (0.1.7)
6
5
  rails (>= 5.0)
7
6
 
8
7
  GEM
9
8
  remote: https://rubygems.org/
10
9
  specs:
11
- actioncable (6.0.3.3)
12
- actionpack (= 6.0.3.3)
10
+ actioncable (6.0.3.4)
11
+ actionpack (= 6.0.3.4)
13
12
  nio4r (~> 2.0)
14
13
  websocket-driver (>= 0.6.1)
15
- actionmailbox (6.0.3.3)
16
- actionpack (= 6.0.3.3)
17
- activejob (= 6.0.3.3)
18
- activerecord (= 6.0.3.3)
19
- activestorage (= 6.0.3.3)
20
- activesupport (= 6.0.3.3)
14
+ actionmailbox (6.0.3.4)
15
+ actionpack (= 6.0.3.4)
16
+ activejob (= 6.0.3.4)
17
+ activerecord (= 6.0.3.4)
18
+ activestorage (= 6.0.3.4)
19
+ activesupport (= 6.0.3.4)
21
20
  mail (>= 2.7.1)
22
- actionmailer (6.0.3.3)
23
- actionpack (= 6.0.3.3)
24
- actionview (= 6.0.3.3)
25
- activejob (= 6.0.3.3)
21
+ actionmailer (6.0.3.4)
22
+ actionpack (= 6.0.3.4)
23
+ actionview (= 6.0.3.4)
24
+ activejob (= 6.0.3.4)
26
25
  mail (~> 2.5, >= 2.5.4)
27
26
  rails-dom-testing (~> 2.0)
28
- actionpack (6.0.3.3)
29
- actionview (= 6.0.3.3)
30
- activesupport (= 6.0.3.3)
27
+ actionpack (6.0.3.4)
28
+ actionview (= 6.0.3.4)
29
+ activesupport (= 6.0.3.4)
31
30
  rack (~> 2.0, >= 2.0.8)
32
31
  rack-test (>= 0.6.3)
33
32
  rails-dom-testing (~> 2.0)
34
33
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
35
- actiontext (6.0.3.3)
36
- actionpack (= 6.0.3.3)
37
- activerecord (= 6.0.3.3)
38
- activestorage (= 6.0.3.3)
39
- activesupport (= 6.0.3.3)
34
+ actiontext (6.0.3.4)
35
+ actionpack (= 6.0.3.4)
36
+ activerecord (= 6.0.3.4)
37
+ activestorage (= 6.0.3.4)
38
+ activesupport (= 6.0.3.4)
40
39
  nokogiri (>= 1.8.5)
41
- actionview (6.0.3.3)
42
- activesupport (= 6.0.3.3)
40
+ actionview (6.0.3.4)
41
+ activesupport (= 6.0.3.4)
43
42
  builder (~> 3.1)
44
43
  erubi (~> 1.4)
45
44
  rails-dom-testing (~> 2.0)
46
45
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
47
- activejob (6.0.3.3)
48
- activesupport (= 6.0.3.3)
46
+ activejob (6.0.3.4)
47
+ activesupport (= 6.0.3.4)
49
48
  globalid (>= 0.3.6)
50
- activemodel (6.0.3.3)
51
- activesupport (= 6.0.3.3)
52
- activerecord (6.0.3.3)
53
- activemodel (= 6.0.3.3)
54
- activesupport (= 6.0.3.3)
55
- activestorage (6.0.3.3)
56
- actionpack (= 6.0.3.3)
57
- activejob (= 6.0.3.3)
58
- activerecord (= 6.0.3.3)
49
+ activemodel (6.0.3.4)
50
+ activesupport (= 6.0.3.4)
51
+ activerecord (6.0.3.4)
52
+ activemodel (= 6.0.3.4)
53
+ activesupport (= 6.0.3.4)
54
+ activestorage (6.0.3.4)
55
+ actionpack (= 6.0.3.4)
56
+ activejob (= 6.0.3.4)
57
+ activerecord (= 6.0.3.4)
59
58
  marcel (~> 0.3.1)
60
- activesupport (6.0.3.3)
59
+ activesupport (6.0.3.4)
61
60
  concurrent-ruby (~> 1.0, >= 1.0.2)
62
61
  i18n (>= 0.7, < 2)
63
62
  minitest (~> 5.1)
@@ -65,22 +64,15 @@ GEM
65
64
  zeitwerk (~> 2.2, >= 2.2.2)
66
65
  builder (3.2.4)
67
66
  coderay (1.1.3)
68
- concurrent-ruby (1.1.7)
67
+ concurrent-ruby (1.1.8)
69
68
  crass (1.0.6)
70
69
  diff-lcs (1.4.4)
71
- dry-configurable (0.11.6)
72
- concurrent-ruby (~> 1.0)
73
- dry-core (~> 0.4, >= 0.4.7)
74
- dry-equalizer (~> 0.2)
75
- dry-core (0.4.9)
76
- concurrent-ruby (~> 1.0)
77
- dry-equalizer (0.3.0)
78
- erubi (1.9.0)
70
+ erubi (1.10.0)
79
71
  globalid (0.4.2)
80
72
  activesupport (>= 4.2.0)
81
- i18n (1.8.5)
73
+ i18n (1.8.9)
82
74
  concurrent-ruby (~> 1.0)
83
- loofah (2.7.0)
75
+ loofah (2.9.0)
84
76
  crass (~> 1.0.2)
85
77
  nokogiri (>= 1.5.9)
86
78
  mail (2.7.1)
@@ -90,45 +82,46 @@ GEM
90
82
  method_source (1.0.0)
91
83
  mimemagic (0.3.5)
92
84
  mini_mime (1.0.2)
93
- mini_portile2 (2.4.0)
94
- minitest (5.14.2)
95
- nio4r (2.5.3)
96
- nokogiri (1.10.10)
97
- mini_portile2 (~> 2.4.0)
85
+ minitest (5.14.4)
86
+ mysql2 (0.5.3)
87
+ nio4r (2.5.7)
88
+ nokogiri (1.11.2-x86_64-darwin)
89
+ racc (~> 1.4)
98
90
  pg (1.2.3)
99
91
  pry (0.13.1)
100
92
  coderay (~> 1.1)
101
93
  method_source (~> 1.0)
94
+ racc (1.5.2)
102
95
  rack (2.2.3)
103
96
  rack-test (1.1.0)
104
97
  rack (>= 1.0, < 3)
105
- rails (6.0.3.3)
106
- actioncable (= 6.0.3.3)
107
- actionmailbox (= 6.0.3.3)
108
- actionmailer (= 6.0.3.3)
109
- actionpack (= 6.0.3.3)
110
- actiontext (= 6.0.3.3)
111
- actionview (= 6.0.3.3)
112
- activejob (= 6.0.3.3)
113
- activemodel (= 6.0.3.3)
114
- activerecord (= 6.0.3.3)
115
- activestorage (= 6.0.3.3)
116
- activesupport (= 6.0.3.3)
98
+ rails (6.0.3.4)
99
+ actioncable (= 6.0.3.4)
100
+ actionmailbox (= 6.0.3.4)
101
+ actionmailer (= 6.0.3.4)
102
+ actionpack (= 6.0.3.4)
103
+ actiontext (= 6.0.3.4)
104
+ actionview (= 6.0.3.4)
105
+ activejob (= 6.0.3.4)
106
+ activemodel (= 6.0.3.4)
107
+ activerecord (= 6.0.3.4)
108
+ activestorage (= 6.0.3.4)
109
+ activesupport (= 6.0.3.4)
117
110
  bundler (>= 1.3.0)
118
- railties (= 6.0.3.3)
111
+ railties (= 6.0.3.4)
119
112
  sprockets-rails (>= 2.0.0)
120
113
  rails-dom-testing (2.0.3)
121
114
  activesupport (>= 4.2.0)
122
115
  nokogiri (>= 1.6)
123
116
  rails-html-sanitizer (1.3.0)
124
117
  loofah (~> 2.3)
125
- railties (6.0.3.3)
126
- actionpack (= 6.0.3.3)
127
- activesupport (= 6.0.3.3)
118
+ railties (6.0.3.4)
119
+ actionpack (= 6.0.3.4)
120
+ activesupport (= 6.0.3.4)
128
121
  method_source
129
122
  rake (>= 0.8.7)
130
123
  thor (>= 0.20.3, < 2.0)
131
- rake (13.0.1)
124
+ rake (13.0.3)
132
125
  rspec (3.9.0)
133
126
  rspec-core (~> 3.9.0)
134
127
  rspec-expectations (~> 3.9.0)
@@ -149,20 +142,21 @@ GEM
149
142
  actionpack (>= 4.0)
150
143
  activesupport (>= 4.0)
151
144
  sprockets (>= 3.0.0)
152
- thor (1.0.1)
145
+ thor (1.1.0)
153
146
  thread_safe (0.3.6)
154
- tzinfo (1.2.7)
147
+ tzinfo (1.2.9)
155
148
  thread_safe (~> 0.1)
156
149
  websocket-driver (0.7.3)
157
150
  websocket-extensions (>= 0.1.0)
158
151
  websocket-extensions (0.1.5)
159
152
  yard (0.9.25)
160
- zeitwerk (2.4.0)
153
+ zeitwerk (2.4.2)
161
154
 
162
155
  PLATFORMS
163
156
  ruby
164
157
 
165
158
  DEPENDENCIES
159
+ mysql2 (~> 0.5.3)
166
160
  pg (>= 0.18)
167
161
  pry (~> 0.13.1)
168
162
  rspec (~> 3.9.0)
@@ -170,4 +164,4 @@ DEPENDENCIES
170
164
  yesql!
171
165
 
172
166
  BUNDLED WITH
173
- 2.1.4
167
+ 2.2.3
data/README.md CHANGED
@@ -70,7 +70,6 @@ YeSQL('top_10_users_in_x_country', { country: 'Cuba', country_id: 1, limit: 6 })
70
70
  ```
71
71
 
72
72
  - If the query doesn't have bindings, but they're provided they're just omitted.
73
- TODO: update this with link to the error.
74
73
  - If the query has bindings, but nothing is provided, it raises a `NotImplementedError` exception.
75
74
 
76
75
 
@@ -145,21 +144,21 @@ YeSQL('users', cache: { expires_in: 1.hour })
145
144
 
146
145
  ## Configuration
147
146
 
148
- For default `YeSQL` looks for the _.sql_ files defined under the `app/yesql/` folder but you can update it to use any folder you need. For that you can create a Ruby file under the `config/initializers/` folder as:
147
+ For default `YeSQL` looks for the _.sql_ files defined under the `app/yesql/` folder but you can update it to use any folder you need. For that you can create a Ruby file under the `config/initializers/` with the following content:
149
148
 
150
149
  ```ruby
151
- YeSQL.config.path = 'path'
150
+ ::YeSQL.configure { |config| config.path = 'path' }
152
151
  ```
153
152
 
154
153
  After saving the file and restarting the server the files are going to be read from the given folder.
155
154
 
156
- You can check at anytime what's the configuration path by inspecting the YeSQL `config` object:
155
+ You can check at anytime what's the configuration path by inspecting the ::YeSQL `config` object:
157
156
 
158
157
  ```ruby
159
- YeSQL.config
160
- # <Dry::Configurable::Config values={:path=>"path"}>
161
- Yesql.config.path
162
- # "path"
158
+ ::YeSQL.config
159
+ # => #<YeSQL::Config::Configuration:0x00007feea1aa2ef8 @path="app/yesql">
160
+ ::YeSQL.config.path
161
+ # => "app/yesql"
163
162
  ```
164
163
 
165
164
 
@@ -167,7 +166,7 @@ Yesql.config.path
167
166
 
168
167
  - Clone the repository.
169
168
  - Install the gem dependencies.
170
- - Make sure to create the database used in the dummy Rails application in the spec/ folder.
169
+ - Make sure to create both databases used in the dummy Rails applications (mysql, pg) in the spec/ folder.
171
170
  - Run the tests.
172
171
 
173
172
  ## Contributing
data/lib/yesql.rb CHANGED
@@ -1,25 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry-configurable'
4
- require 'pry'
3
+ require 'yesql/statement'
5
4
  require 'yesql/version'
5
+ require 'yesql/config/configuration'
6
6
  require 'yesql/query/performer'
7
7
  require 'yesql/errors/cache_expiration_error'
8
8
  require 'yesql/errors/file_path_does_not_exist_error'
9
9
  require 'yesql/errors/no_bindings_provided_error'
10
10
  require 'yesql/errors/output_argument_error'
11
- require 'yesql/bindings/binder'
12
11
 
13
12
  module YeSQL
14
- extend ::Dry::Configurable
15
-
13
+ include ::YeSQL::Config
16
14
  include ::YeSQL::Errors::CacheExpirationError
17
15
  include ::YeSQL::Errors::FilePathDoesNotExistError
18
16
  include ::YeSQL::Errors::NoBindingsProvidedError
19
17
  include ::YeSQL::Errors::OutputArgumentError
20
18
 
21
- setting :path, 'app/yesql'
22
-
23
19
  BIND_REGEX = /(?<!:):(\w+)(?=\b)/.freeze
24
20
 
25
21
  # rubocop:disable Naming/MethodName
@@ -44,7 +40,7 @@ module YeSQL
44
40
  def execute(bindings, cache, file_path, output, options)
45
41
  ::YeSQL::Query::Performer.new(
46
42
  bindings: bindings,
47
- bind_statement: ::YeSQL::Bindings::Binder.bind_statement(file_path, bindings),
43
+ bind_statement: ::YeSQL::Statement.new(bindings, file_path),
48
44
  cache: cache,
49
45
  file_path: file_path,
50
46
  output: output,
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yesql'
4
+ require 'yesql/common/adapter'
4
5
 
5
- module YeSQL
6
+ module ::YeSQL
6
7
  module Bindings
7
8
  class Extract
9
+ include ::YeSQL::Common::Adapter
10
+
8
11
  def initialize(indexed_bindings, hash, index, value)
9
12
  @indexed_bindings = indexed_bindings
10
13
  @hash = hash
@@ -19,9 +22,15 @@ module YeSQL
19
22
  end
20
23
 
21
24
  def bind_vars
22
- return "$#{last_val}" unless array?
25
+ if mysql?
26
+ return '?' unless array?
27
+
28
+ Array.new(value.size, '?').join(', ')
29
+ elsif pg?
30
+ return "$#{last_val}" unless array?
23
31
 
24
- value.map.with_index(bind_index) { |_, i| "$#{i}" }.join(', ')
32
+ value.map.with_index(bind_index) { |_, i| "$#{i}" }.join(', ')
33
+ end
25
34
  end
26
35
 
27
36
  def last_val
@@ -0,0 +1,61 @@
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?
27
+ ::Rails::VERSION::MAJOR == 5
28
+ end
29
+
30
+ def mysql_rails5_binds
31
+ statement_binds
32
+ .map(&:first)
33
+ .flatten(1)
34
+ .each_slice(2)
35
+ .flat_map do |first, last|
36
+ next [first, last].compact.map(&:last) if first.is_a?(Array)
37
+
38
+ last
39
+ end
40
+ end
41
+
42
+ def mysql_binds
43
+ statement_binds
44
+ .map(&:first)
45
+ .flatten
46
+ .each_slice(2)
47
+ .to_a
48
+ end
49
+
50
+ def pg_binds
51
+ statement_binds
52
+ .sort_by { |_, position| position.to_s.tr('$', '').to_i }
53
+ .uniq
54
+ .map(&:first)
55
+ .flatten
56
+ .each_slice(2)
57
+ .to_a
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module ::YeSQL
6
+ module Common
7
+ module Adapter
8
+ extend Forwardable
9
+
10
+ # `adapter` might be a complex object, but
11
+ # for the sake of brevity it's just a string
12
+ def adapter
13
+ ::ActiveRecord::Base.connection.adapter_name
14
+ end
15
+
16
+ def mysql?
17
+ adapter == 'Mysql2'
18
+ end
19
+
20
+ def pg?
21
+ adapter == 'PostgreSQL'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ::YeSQL
4
+ module Config
5
+ class Configuration
6
+ attr_accessor :path
7
+
8
+ DEFAULT_PATH = 'app/yesql'
9
+
10
+ def initialize
11
+ @path = DEFAULT_PATH
12
+ end
13
+ end
14
+ end
15
+
16
+ class << self
17
+ def config
18
+ @config ||= ::YeSQL::Configuration.new
19
+ end
20
+
21
+ def configure
22
+ yield config if block_given?
23
+ end
24
+
25
+ def reset_config
26
+ tap do |conf|
27
+ conf.configure do |configuration|
28
+ configuration.path = ::YeSQL::Configuration::DEFAULT_PATH
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module YeSQL
3
+ require 'yesql/utils/read'
4
+
5
+ module ::YeSQL
4
6
  module Errors
5
7
  module NoBindingsProvidedError
6
8
  def validate_statement_bindings(binds, file_path)
7
9
  return unless statement_binds(file_path).size.positive?
8
10
 
9
11
  format(MESSAGE, renderable_statement_binds(file_path)).tap do |message|
10
- raise ArgumentError, message unless binds.is_a?(Hash) && !binds.empty?
12
+ raise ::ArgumentError, message unless binds.is_a?(::Hash) && !binds.empty?
11
13
  end
12
14
  end
13
15
 
@@ -0,0 +1,34 @@
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?
16
+ output == 'columns'
17
+ end
18
+
19
+ def rows?
20
+ output == 'rows'
21
+ end
22
+
23
+ def hash?
24
+ output == 'hash'
25
+ end
26
+
27
+ def_delegator(:output, :to_sym)
28
+
29
+ private
30
+
31
+ attr_reader :output
32
+ end
33
+ end
34
+ end
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yesql'
4
- require 'forwardable'
5
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'
6
10
 
7
11
  module YeSQL
8
12
  module Query
9
13
  class Performer
10
- extend Forwardable
11
-
12
14
  include ::YeSQL::Bindings::Utils
13
15
 
14
16
  # rubocop:disable Metrics/ParameterLists
@@ -21,19 +23,18 @@ module YeSQL
21
23
  @bind_statement = bind_statement
22
24
  @cache = cache
23
25
  @cache_key = cache[:key] || file_path
24
- @connection = ActiveRecord::Base.connection
25
26
  @expires_in = cache[:expires_in]
26
27
  @file_path = file_path
27
28
  @named_bindings = bindings.transform_keys(&:to_sym)
28
- @output = output
29
+ @output = ::YeSQL::Params::Output.new(output)
29
30
  @prepare = prepare
30
31
  end
31
32
  # rubocop:enable Metrics/ParameterLists
32
33
 
33
34
  def call
34
- return modified_output if cache.empty?
35
+ return transformed_result if cache.empty?
35
36
 
36
- Rails.cache.fetch(cache_key, expires_in: expires_in) { modified_output }
37
+ Rails.cache.fetch(cache_key, expires_in: expires_in) { transformed_result }
37
38
  end
38
39
 
39
40
  private
@@ -41,7 +42,6 @@ module YeSQL
41
42
  attr_reader :bind_statement,
42
43
  :cache,
43
44
  :cache_key,
44
- :connection,
45
45
  :expires_in,
46
46
  :file_path,
47
47
  :named_bindings,
@@ -49,37 +49,24 @@ module YeSQL
49
49
  :prepare,
50
50
  :rows
51
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
52
+ def transformed_result
53
+ @transformed_result ||=
54
+ ::YeSQL::Query::TransformResult.new(output: output, result: query_result).call
64
55
  end
65
56
 
66
57
  def query_result
67
- @query_result ||= connection.exec_query(bind_statement, file_path, binds, prepare: prepare)
58
+ @query_result ||= ::YeSQL::Query::Result.new(binds: binds,
59
+ bind_statement: bind_statement,
60
+ file_path: file_path,
61
+ prepare: prepare).call
68
62
  end
69
63
 
70
- def extractor
71
- ::YeSQL::Bindings::Extractor.new(bindings: named_bindings).call
64
+ def binds
65
+ ::YeSQL::Bindings::Transformed.new(statement_binds: statement_binds(extractor)).call
72
66
  end
73
67
 
74
- # TODO: move this somewhere else.
75
- 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
68
+ def extractor
69
+ ::YeSQL::Bindings::Extractor.new(bindings: named_bindings).call
83
70
  end
84
71
  end
85
72
  end
@@ -0,0 +1,46 @@
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(binds: [], bind_statement:, file_path:, prepare:)
15
+ @binds = binds
16
+ @bind_statement = bind_statement
17
+ @connection = ActiveRecord::Base.connection
18
+ @file_path = file_path
19
+ @prepare_option = prepare
20
+ end
21
+
22
+ def call
23
+ return view_result if view?
24
+ return rails5_result if ::Rails::VERSION::MAJOR == 5 && mysql?
25
+
26
+ exec_query(bound, file_path, binds, prepare: prepare_option)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :binds, :bind_statement, :connection, :file_path, :prepare_option
32
+
33
+ def_delegators(:bind_statement, :bound, :to_s, :view?)
34
+ def_delegators(:connection, :exec_query, :raw_connection)
35
+ def_delegators(:raw_connection, :prepare)
36
+
37
+ def view_result
38
+ exec_query(bound)
39
+ end
40
+
41
+ def rails5_result
42
+ prepare(bound).execute(*binds)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,58 @@
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::VERSION::MAJOR == 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, :rows, :to_a)
36
+ def_delegators(:output, :columns?, :hash?, :rows?)
37
+
38
+ def rows_values
39
+ to_a.map { |e| e.respond_to?(:values) ? e.values : e }
40
+ end
41
+
42
+ def array_of_symbol_hashes
43
+ to_a.tap { |rows| break hashed_rows(rows) if ::Rails::VERSION::MAJOR == 5 }
44
+ .map { |e| e.respond_to?(:symbolize_keys) ? e.symbolize_keys : e }
45
+ end
46
+
47
+ def hashed_rows(rows)
48
+ rows.map { |row| columns.zip(row).to_h }
49
+ end
50
+
51
+ def columns
52
+ return result.fields if result.respond_to?(:fields)
53
+
54
+ result.columns
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ require 'yesql/utils/read'
6
+ require 'yesql/bindings/extractor'
7
+ require 'yesql/common/adapter'
8
+
9
+ module ::YeSQL
10
+ class Statement
11
+ extend Forwardable
12
+
13
+ include ::YeSQL::Common::Adapter
14
+
15
+ def initialize(bindings = {}, file_path)
16
+ @bindings = bindings
17
+ @connection = ::ActiveRecord::Base.connection
18
+ @file_path = file_path
19
+ end
20
+
21
+ def bound
22
+ to_s.gsub(::YeSQL::BIND_REGEX) do |match|
23
+ extractor[match[/(\w+)/].to_sym].tap do |extract|
24
+ break quote(extract[:value]) if view?
25
+
26
+ break extract[:bind][:vars]
27
+ end
28
+ end
29
+ end
30
+
31
+ def to_s
32
+ @to_s ||= ::YeSQL::Utils::Read.statement(file_path, readable: true)
33
+ end
34
+
35
+ def view?
36
+ to_s =~ /^create\s.*view\s/i
37
+ end
38
+
39
+ private
40
+
41
+ def_delegator(:connection, :quote)
42
+
43
+ attr_reader :bindings, :connection, :file_path
44
+
45
+ def extractor
46
+ ::YeSQL::Bindings::Extractor.new(bindings: bindings).call
47
+ end
48
+ end
49
+ end
data/lib/yesql/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module YeSQL
4
- VERSION = '0.1.3'
4
+ VERSION = '0.1.8'
5
5
  end
data/yesql.gemspec CHANGED
@@ -20,8 +20,8 @@ Gem::Specification.new do |spec|
20
20
  spec.bindir = 'exe'
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ['lib']
23
- spec.add_dependency 'dry-configurable', '~> 0.11.6'
24
23
  spec.add_dependency 'rails', '>= 5.0'
24
+ spec.add_development_dependency 'mysql2', '~> 0.5.3'
25
25
  spec.add_development_dependency 'pg', '>= 0.18'
26
26
  spec.add_development_dependency 'pry', '~> 0.13.1'
27
27
  spec.add_development_dependency 'rspec', '~> 3.9.0'
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yesql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastián Palma
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-15 00:00:00.000000000 Z
11
+ date: 2021-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: dry-configurable
14
+ name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.11.6
19
+ version: '5.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.11.6
26
+ version: '5.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rails
28
+ name: mysql2
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '5.0'
34
- type: :runtime
33
+ version: 0.5.3
34
+ type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '5.0'
40
+ version: 0.5.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: pg
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -87,6 +87,8 @@ executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
+ - ".gitignore"
91
+ - ".rubocop.yml"
90
92
  - Gemfile
91
93
  - Gemfile.lock
92
94
  - LICENSE.txt
@@ -95,15 +97,21 @@ files:
95
97
  - bin/console
96
98
  - bin/setup
97
99
  - lib/yesql.rb
98
- - lib/yesql/bindings/binder.rb
99
100
  - lib/yesql/bindings/extract.rb
100
101
  - lib/yesql/bindings/extractor.rb
102
+ - lib/yesql/bindings/transformed.rb
101
103
  - lib/yesql/bindings/utils.rb
104
+ - lib/yesql/common/adapter.rb
105
+ - lib/yesql/config/configuration.rb
102
106
  - lib/yesql/errors/cache_expiration_error.rb
103
107
  - lib/yesql/errors/file_path_does_not_exist_error.rb
104
108
  - lib/yesql/errors/no_bindings_provided_error.rb
105
109
  - lib/yesql/errors/output_argument_error.rb
110
+ - lib/yesql/params/output.rb
106
111
  - lib/yesql/query/performer.rb
112
+ - lib/yesql/query/result.rb
113
+ - lib/yesql/query/transform_result.rb
114
+ - lib/yesql/statement.rb
107
115
  - lib/yesql/utils/read.rb
108
116
  - lib/yesql/version.rb
109
117
  - yesql.gemspec
@@ -129,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
137
  - !ruby/object:Gem::Version
130
138
  version: '0'
131
139
  requirements: []
132
- rubygems_version: 3.1.2
140
+ rubygems_version: 3.2.3
133
141
  signing_key:
134
142
  specification_version: 4
135
143
  summary: Ruby library to use SQL
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yesql/utils/read'
4
- require 'yesql/bindings/extractor'
5
-
6
- module YeSQL
7
- module Bindings
8
- class Binder
9
- def self.bind_statement(file_path, bindings)
10
- ::YeSQL::Bindings::Extractor.new(bindings: bindings).call.tap do |extractor|
11
- break ::YeSQL::Utils::Read.statement(file_path, readable: true)
12
- .gsub(::YeSQL::BIND_REGEX) do |match|
13
- extractor[match[/(\w+)/].to_sym][:bind][:vars]
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end