yesql 0.1.2 → 0.1.7
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/.gitignore +4 -0
- data/.rubocop.yml +20 -0
- data/Gemfile.lock +54 -60
- data/README.md +8 -9
- data/lib/yesql.rb +4 -8
- data/lib/yesql/bindings/extract.rb +12 -3
- data/lib/yesql/bindings/transformed.rb +61 -0
- data/lib/yesql/common/adapter.rb +25 -0
- data/lib/yesql/config/configuration.rb +33 -0
- data/lib/yesql/errors/no_bindings_provided_error.rb +4 -2
- data/lib/yesql/params/output.rb +34 -0
- data/lib/yesql/query/performer.rb +20 -29
- data/lib/yesql/query/result.rb +46 -0
- data/lib/yesql/query/transform_result.rb +58 -0
- data/lib/yesql/statement.rb +49 -0
- data/lib/yesql/version.rb +1 -1
- data/yesql.gemspec +1 -1
- metadata +22 -14
- data/lib/yesql/bindings/binder.rb +0 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 180f75c325ecfc85f0d95e0d0a9422fcde92db273487bd19a7f03318c3206f16
|
|
4
|
+
data.tar.gz: '00429a2c7880d6546e82b5c530d097be14e524ed161a81d7c75b55c61f86f2b7'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6a13774afd3b568927652026d00ac25d1b521c4eebcdcf4d2f4bc9d3de86652613322d449e30f276eabfebcb2aaa044bdc66abc3757f0f759533b0fcf5c53868
|
|
7
|
+
data.tar.gz: 3017fb1e072485f1c3d469517c1e326d74c32f2d14873a8801c78da8d0bb1f49459a81f6abc64b9c0fa1680dd8799cd12328496ec0077fa2823e24e204f80c1a
|
data/.gitignore
ADDED
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.
|
|
5
|
-
dry-configurable (~> 0.11.6)
|
|
4
|
+
yesql (0.1.6)
|
|
6
5
|
rails (>= 5.0)
|
|
7
6
|
|
|
8
7
|
GEM
|
|
9
8
|
remote: https://rubygems.org/
|
|
10
9
|
specs:
|
|
11
|
-
actioncable (6.0.3.
|
|
12
|
-
actionpack (= 6.0.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.
|
|
16
|
-
actionpack (= 6.0.3.
|
|
17
|
-
activejob (= 6.0.3.
|
|
18
|
-
activerecord (= 6.0.3.
|
|
19
|
-
activestorage (= 6.0.3.
|
|
20
|
-
activesupport (= 6.0.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.
|
|
23
|
-
actionpack (= 6.0.3.
|
|
24
|
-
actionview (= 6.0.3.
|
|
25
|
-
activejob (= 6.0.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.
|
|
29
|
-
actionview (= 6.0.3.
|
|
30
|
-
activesupport (= 6.0.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.
|
|
36
|
-
actionpack (= 6.0.3.
|
|
37
|
-
activerecord (= 6.0.3.
|
|
38
|
-
activestorage (= 6.0.3.
|
|
39
|
-
activesupport (= 6.0.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.
|
|
42
|
-
activesupport (= 6.0.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.
|
|
48
|
-
activesupport (= 6.0.3.
|
|
46
|
+
activejob (6.0.3.4)
|
|
47
|
+
activesupport (= 6.0.3.4)
|
|
49
48
|
globalid (>= 0.3.6)
|
|
50
|
-
activemodel (6.0.3.
|
|
51
|
-
activesupport (= 6.0.3.
|
|
52
|
-
activerecord (6.0.3.
|
|
53
|
-
activemodel (= 6.0.3.
|
|
54
|
-
activesupport (= 6.0.3.
|
|
55
|
-
activestorage (6.0.3.
|
|
56
|
-
actionpack (= 6.0.3.
|
|
57
|
-
activejob (= 6.0.3.
|
|
58
|
-
activerecord (= 6.0.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.
|
|
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)
|
|
@@ -68,13 +67,6 @@ GEM
|
|
|
68
67
|
concurrent-ruby (1.1.7)
|
|
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
70
|
erubi (1.9.0)
|
|
79
71
|
globalid (0.4.2)
|
|
80
72
|
activesupport (>= 4.2.0)
|
|
@@ -92,7 +84,8 @@ GEM
|
|
|
92
84
|
mini_mime (1.0.2)
|
|
93
85
|
mini_portile2 (2.4.0)
|
|
94
86
|
minitest (5.14.2)
|
|
95
|
-
|
|
87
|
+
mysql2 (0.5.3)
|
|
88
|
+
nio4r (2.5.4)
|
|
96
89
|
nokogiri (1.10.10)
|
|
97
90
|
mini_portile2 (~> 2.4.0)
|
|
98
91
|
pg (1.2.3)
|
|
@@ -102,29 +95,29 @@ GEM
|
|
|
102
95
|
rack (2.2.3)
|
|
103
96
|
rack-test (1.1.0)
|
|
104
97
|
rack (>= 1.0, < 3)
|
|
105
|
-
rails (6.0.3.
|
|
106
|
-
actioncable (= 6.0.3.
|
|
107
|
-
actionmailbox (= 6.0.3.
|
|
108
|
-
actionmailer (= 6.0.3.
|
|
109
|
-
actionpack (= 6.0.3.
|
|
110
|
-
actiontext (= 6.0.3.
|
|
111
|
-
actionview (= 6.0.3.
|
|
112
|
-
activejob (= 6.0.3.
|
|
113
|
-
activemodel (= 6.0.3.
|
|
114
|
-
activerecord (= 6.0.3.
|
|
115
|
-
activestorage (= 6.0.3.
|
|
116
|
-
activesupport (= 6.0.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.
|
|
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.
|
|
126
|
-
actionpack (= 6.0.3.
|
|
127
|
-
activesupport (= 6.0.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)
|
|
@@ -163,6 +156,7 @@ 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)
|
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/`
|
|
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
|
-
#
|
|
161
|
-
|
|
162
|
-
# "
|
|
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
|
|
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 '
|
|
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
|
-
|
|
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::
|
|
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
|
-
|
|
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
|
-
|
|
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].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
|
-
|
|
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
|
|
35
|
+
return transformed_result if cache.empty?
|
|
35
36
|
|
|
36
|
-
Rails.cache.fetch(cache_key, expires_in: expires_in) {
|
|
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,33 +49,24 @@ module YeSQL
|
|
|
49
49
|
:prepare,
|
|
50
50
|
:rows
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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 ||=
|
|
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
64
|
def binds
|
|
71
|
-
::YeSQL::Bindings::
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
.each_slice(2)
|
|
77
|
-
.to_a
|
|
78
|
-
end
|
|
65
|
+
::YeSQL::Bindings::Transformed.new(statement_binds: statement_binds(extractor)).call
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def extractor
|
|
69
|
+
::YeSQL::Bindings::Extractor.new(bindings: named_bindings).call
|
|
79
70
|
end
|
|
80
71
|
end
|
|
81
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
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.
|
|
4
|
+
version: 0.1.7
|
|
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-
|
|
11
|
+
date: 2020-11-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: rails
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0
|
|
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
|
|
26
|
+
version: '5.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: mysql2
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
34
|
-
type: :
|
|
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:
|
|
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
|
|
@@ -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
|