yesql 0.1.7 → 0.2.3

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: 180f75c325ecfc85f0d95e0d0a9422fcde92db273487bd19a7f03318c3206f16
4
- data.tar.gz: '00429a2c7880d6546e82b5c530d097be14e524ed161a81d7c75b55c61f86f2b7'
3
+ metadata.gz: f38a878a124272c8ed8f51d723cba49704d2e7b60b28920c562fdbc8e1d68836
4
+ data.tar.gz: dbfa7776c1eb0eeae767023504cb741dd05052fd992b3b01a218f849917d7696
5
5
  SHA512:
6
- metadata.gz: 6a13774afd3b568927652026d00ac25d1b521c4eebcdcf4d2f4bc9d3de86652613322d449e30f276eabfebcb2aaa044bdc66abc3757f0f759533b0fcf5c53868
7
- data.tar.gz: 3017fb1e072485f1c3d469517c1e326d74c32f2d14873a8801c78da8d0bb1f49459a81f6abc64b9c0fa1680dd8799cd12328496ec0077fa2823e24e204f80c1a
6
+ metadata.gz: b781599e0e8d5f6a84bec509737595269f28e6347284046f2a7d767ff14256b79a8658643b0e077a453ce08bf4d23766e2eebbd1bcc19b952b43f3eeda94b87b
7
+ data.tar.gz: 6ac1827d64222972e9fd70bbdb216576581c86dd54a93824d20236e451720ae035ee54ad47912270faa823d8ff30d471353e9a69d123fa1d2544315067683481
data/.gitignore CHANGED
@@ -2,3 +2,6 @@ spec/minimalpg/log/*
2
2
  spec/minimalmysql/log/*
3
3
  spec/minimalpg/db/*
4
4
  spec/minimalmysql/db/*
5
+
6
+ *.un~
7
+ *.swp
data/Gemfile CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
6
 
7
7
  group :docs do
8
- gem 'yard'
8
+ gem "yard"
9
9
  end
data/Gemfile.lock CHANGED
@@ -1,159 +1,55 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- yesql (0.1.6)
5
- rails (>= 5.0)
4
+ yesql (0.1.8)
5
+ activerecord (>= 4.0.0.beta1)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- actioncable (6.0.3.4)
11
- actionpack (= 6.0.3.4)
12
- nio4r (~> 2.0)
13
- websocket-driver (>= 0.6.1)
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)
20
- mail (>= 2.7.1)
21
- actionmailer (6.0.3.4)
22
- actionpack (= 6.0.3.4)
23
- actionview (= 6.0.3.4)
24
- activejob (= 6.0.3.4)
25
- mail (~> 2.5, >= 2.5.4)
26
- rails-dom-testing (~> 2.0)
27
- actionpack (6.0.3.4)
28
- actionview (= 6.0.3.4)
29
- activesupport (= 6.0.3.4)
30
- rack (~> 2.0, >= 2.0.8)
31
- rack-test (>= 0.6.3)
32
- rails-dom-testing (~> 2.0)
33
- rails-html-sanitizer (~> 1.0, >= 1.2.0)
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)
39
- nokogiri (>= 1.8.5)
40
- actionview (6.0.3.4)
41
- activesupport (= 6.0.3.4)
42
- builder (~> 3.1)
43
- erubi (~> 1.4)
44
- rails-dom-testing (~> 2.0)
45
- rails-html-sanitizer (~> 1.1, >= 1.2.0)
46
- activejob (6.0.3.4)
47
- activesupport (= 6.0.3.4)
48
- globalid (>= 0.3.6)
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)
58
- marcel (~> 0.3.1)
59
- activesupport (6.0.3.4)
10
+ activemodel (6.1.3.1)
11
+ activesupport (= 6.1.3.1)
12
+ activerecord (6.1.3.1)
13
+ activemodel (= 6.1.3.1)
14
+ activesupport (= 6.1.3.1)
15
+ activesupport (6.1.3.1)
60
16
  concurrent-ruby (~> 1.0, >= 1.0.2)
61
- i18n (>= 0.7, < 2)
62
- minitest (~> 5.1)
63
- tzinfo (~> 1.1)
64
- zeitwerk (~> 2.2, >= 2.2.2)
65
- builder (3.2.4)
17
+ i18n (>= 1.6, < 2)
18
+ minitest (>= 5.1)
19
+ tzinfo (~> 2.0)
20
+ zeitwerk (~> 2.3)
66
21
  coderay (1.1.3)
67
- concurrent-ruby (1.1.7)
68
- crass (1.0.6)
22
+ concurrent-ruby (1.1.8)
69
23
  diff-lcs (1.4.4)
70
- erubi (1.9.0)
71
- globalid (0.4.2)
72
- activesupport (>= 4.2.0)
73
- i18n (1.8.5)
24
+ i18n (1.8.10)
74
25
  concurrent-ruby (~> 1.0)
75
- loofah (2.7.0)
76
- crass (~> 1.0.2)
77
- nokogiri (>= 1.5.9)
78
- mail (2.7.1)
79
- mini_mime (>= 0.1.1)
80
- marcel (0.3.3)
81
- mimemagic (~> 0.3.2)
82
26
  method_source (1.0.0)
83
- mimemagic (0.3.5)
84
- mini_mime (1.0.2)
85
- mini_portile2 (2.4.0)
86
- minitest (5.14.2)
27
+ minitest (5.14.4)
87
28
  mysql2 (0.5.3)
88
- nio4r (2.5.4)
89
- nokogiri (1.10.10)
90
- mini_portile2 (~> 2.4.0)
91
29
  pg (1.2.3)
92
30
  pry (0.13.1)
93
31
  coderay (~> 1.1)
94
32
  method_source (~> 1.0)
95
- rack (2.2.3)
96
- rack-test (1.1.0)
97
- rack (>= 1.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)
110
- bundler (>= 1.3.0)
111
- railties (= 6.0.3.4)
112
- sprockets-rails (>= 2.0.0)
113
- rails-dom-testing (2.0.3)
114
- activesupport (>= 4.2.0)
115
- nokogiri (>= 1.6)
116
- rails-html-sanitizer (1.3.0)
117
- loofah (~> 2.3)
118
- railties (6.0.3.4)
119
- actionpack (= 6.0.3.4)
120
- activesupport (= 6.0.3.4)
121
- method_source
122
- rake (>= 0.8.7)
123
- thor (>= 0.20.3, < 2.0)
124
- rake (13.0.1)
125
33
  rspec (3.9.0)
126
34
  rspec-core (~> 3.9.0)
127
35
  rspec-expectations (~> 3.9.0)
128
36
  rspec-mocks (~> 3.9.0)
129
- rspec-core (3.9.2)
37
+ rspec-core (3.9.3)
130
38
  rspec-support (~> 3.9.3)
131
- rspec-expectations (3.9.2)
39
+ rspec-expectations (3.9.4)
132
40
  diff-lcs (>= 1.2.0, < 2.0)
133
41
  rspec-support (~> 3.9.0)
134
42
  rspec-mocks (3.9.1)
135
43
  diff-lcs (>= 1.2.0, < 2.0)
136
44
  rspec-support (~> 3.9.0)
137
- rspec-support (3.9.3)
138
- sprockets (4.0.2)
45
+ rspec-support (3.9.4)
46
+ tzinfo (2.0.4)
139
47
  concurrent-ruby (~> 1.0)
140
- rack (> 1, < 3)
141
- sprockets-rails (3.2.2)
142
- actionpack (>= 4.0)
143
- activesupport (>= 4.0)
144
- sprockets (>= 3.0.0)
145
- thor (1.0.1)
146
- thread_safe (0.3.6)
147
- tzinfo (1.2.7)
148
- thread_safe (~> 0.1)
149
- websocket-driver (0.7.3)
150
- websocket-extensions (>= 0.1.0)
151
- websocket-extensions (0.1.5)
152
- yard (0.9.25)
153
- zeitwerk (2.4.0)
48
+ yard (0.9.26)
49
+ zeitwerk (2.4.2)
154
50
 
155
51
  PLATFORMS
156
- ruby
52
+ x86_64-linux
157
53
 
158
54
  DEPENDENCIES
159
55
  mysql2 (~> 0.5.3)
@@ -164,4 +60,4 @@ DEPENDENCIES
164
60
  yesql!
165
61
 
166
62
  BUNDLED WITH
167
- 2.1.4
63
+ 2.2.3
data/README.md CHANGED
@@ -120,28 +120,6 @@ ActiveRecord::Base.connection.execute('SELECT * FROM pg_prepared_statements').to
120
120
  ```
121
121
 
122
122
 
123
- #### `cache`
124
-
125
- If you need to cache your query, you can use the `cache` option. `cache` must be a Hash containing __at least__ the `expires_in` key with an `ActiveSupport::Duration` object, e.g:
126
-
127
- ```ruby
128
- YeSQL('users', cache: { key: 'users', expires_in: 1.hour })
129
- ```
130
-
131
- That's enough to cache the result of the query for 1 hour with the cache key "users".
132
-
133
- If no `key` key/value is used, then the cache key is the name of the file containing the SQL code, and
134
-
135
- ```ruby
136
- YeSQL('users', cache: { key: 'users', expires_in: 1.hour })
137
- ```
138
-
139
- is the same as
140
-
141
- ```ruby
142
- YeSQL('users', cache: { expires_in: 1.hour })
143
- ```
144
-
145
123
  ## Configuration
146
124
 
147
125
  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:
@@ -173,6 +151,12 @@ You can check at anytime what's the configuration path by inspecting the ::YeSQL
173
151
 
174
152
  Bug reports and pull requests are welcome on GitHub at https://github.com/sebastian-palma/yesql. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/sebastian-palma/yesql/blob/master/CODE_OF_CONDUCT.md).
175
153
 
154
+ ## TODO
155
+
156
+ - [ ] Allow comments in .sql files.
157
+ - [ ] Improve errors.
158
+ - [ ] Auto convert `x IN (xs)` queries to `(x = x' OR x = x'')`.
159
+
176
160
 
177
161
  ## License
178
162
 
data/Rakefile CHANGED
@@ -1 +1,3 @@
1
1
  # frozen_string_literal: true
2
+
3
+ require "standard/rake"
data/lib/yesql.rb CHANGED
@@ -1,50 +1,40 @@
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 "yesql/statement"
4
+ require "yesql/version"
5
+ require "yesql/config/configuration"
6
+ require "yesql/query/performer"
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
+
11
+ module ::YeSQL
13
12
  include ::YeSQL::Config
14
- include ::YeSQL::Errors::CacheExpirationError
15
- include ::YeSQL::Errors::FilePathDoesNotExistError
16
- include ::YeSQL::Errors::NoBindingsProvidedError
17
- include ::YeSQL::Errors::OutputArgumentError
18
13
 
19
- BIND_REGEX = /(?<!:):(\w+)(?=\b)/.freeze
14
+ BIND_REGEX = /(?<!:):(\w+)(?=\b)/
20
15
 
21
- # rubocop:disable Naming/MethodName
22
- def YeSQL(file_path, bindings = {}, options = {})
16
+ def YeSQL(file_path, binds = {}, options = {})
23
17
  output = options[:output] || :rows
24
- cache = options[:cache] || {}
25
18
 
26
- validate(bindings, cache, file_path, output)
27
- execute(bindings, cache, file_path, output, options)
19
+ validate(binds, file_path, output)
20
+ execute(binds, file_path, output, options[:prepare])
28
21
  end
29
- # rubocop:enable Naming/MethodName
30
22
 
31
23
  private
32
24
 
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?
25
+ def validate(binds, file_path, output)
26
+ ::YeSQL::Errors::FilePathDoesNotExistError.new(file_path).validate_file_path_existence
27
+ ::YeSQL::Errors::NoBindingsProvidedError.new(binds, file_path).validate_statement_bindings
28
+ ::YeSQL::Errors::OutputArgumentError.new(output).validate_output_options
38
29
  end
39
30
 
40
- def execute(bindings, cache, file_path, output, options)
31
+ def execute(binds, file_path, output, prepare)
41
32
  ::YeSQL::Query::Performer.new(
42
- bindings: bindings,
43
- bind_statement: ::YeSQL::Statement.new(bindings, file_path),
44
- cache: cache,
33
+ bindings: binds,
34
+ bind_statement: ::YeSQL::Statement.new(binds, file_path),
45
35
  file_path: file_path,
46
36
  output: output,
47
- prepare: options[:prepare]
37
+ prepare: prepare
48
38
  ).call
49
39
  end
50
40
  end
@@ -1,17 +1,17 @@
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
 
@@ -23,13 +23,13 @@ module ::YeSQL
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) { |_, i| "$#{i}" }.join(", ")
33
33
  end
34
34
  end
35
35
 
@@ -48,7 +48,7 @@ module ::YeSQL
48
48
  attr_reader :hash, :index, :indexed_bindings, :value
49
49
 
50
50
  def current_val_size
51
- return value.size if array?
51
+ return size if array?
52
52
 
53
53
  1
54
54
  end
@@ -72,6 +72,10 @@ module ::YeSQL
72
72
 
73
73
  prev_last_val + 1
74
74
  end
75
+
76
+ def size
77
+ value.size
78
+ end
75
79
  end
76
80
  end
77
81
  end
@@ -1,38 +1,41 @@
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 |extract|
27
+ break {
28
+ bind: {
29
+ vals: extract.bind_vals,
30
+ vars: extract.bind_vars
31
+ },
32
+ last_val: extract.last_val,
33
+ match: key,
34
+ prev: extract.prev,
35
+ value: value
36
+ }
37
+ end
38
+ end
36
39
  end
37
40
  end
38
41
  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
@@ -24,16 +24,15 @@ module ::YeSQL
24
24
  attr_reader :statement_binds
25
25
 
26
26
  def rails5?
27
- ::Rails::VERSION::MAJOR == 5
27
+ ::ActiveRecord::VERSION::MAJOR == 5
28
28
  end
29
29
 
30
30
  def mysql_rails5_binds
31
31
  statement_binds
32
- .map(&:first)
33
- .flatten(1)
32
+ .flat_map(&:first)
34
33
  .each_slice(2)
35
34
  .flat_map do |first, last|
36
- next [first, last].map(&:last) if first.is_a?(Array)
35
+ next [first, last].compact.map(&:last) if first.is_a?(Array)
37
36
 
38
37
  last
39
38
  end
@@ -49,7 +48,7 @@ module ::YeSQL
49
48
 
50
49
  def pg_binds
51
50
  statement_binds
52
- .sort_by { |_, position| position.to_s.tr('$', '').to_i }
51
+ .sort_by { |_, position| position.to_s.tr("$", "").to_i }
53
52
  .uniq
54
53
  .map(&:first)
55
54
  .flatten
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module YeSQL
3
+ module ::YeSQL
4
4
  module Bindings
5
5
  module Utils
6
+ include ::YeSQL::Utils::Read
7
+
6
8
  def statement_binds(extractor)
7
- ::YeSQL::Utils::Read.statement(file_path, readable: true)
8
- .scan(::YeSQL::BIND_REGEX).map do |(bind)|
9
- extractor[bind.to_sym][:bind].values_at(:vals, :vars)
10
- end
9
+ statement(readable: true)
10
+ .scan(::YeSQL::BIND_REGEX)
11
+ .map(&:first)
12
+ .map { extractor[_1.to_sym][:bind].values_at(:vals, :vars) }
11
13
  end
12
14
  end
13
15
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
3
+ require "forwardable"
4
4
 
5
5
  module ::YeSQL
6
6
  module Common
7
7
  module Adapter
8
- extend Forwardable
8
+ extend ::Forwardable
9
9
 
10
10
  # `adapter` might be a complex object, but
11
11
  # for the sake of brevity it's just a string
@@ -14,11 +14,11 @@ module ::YeSQL
14
14
  end
15
15
 
16
16
  def mysql?
17
- adapter == 'Mysql2'
17
+ adapter == "Mysql2"
18
18
  end
19
19
 
20
20
  def pg?
21
- adapter == 'PostgreSQL'
21
+ adapter == "PostgreSQL"
22
22
  end
23
23
  end
24
24
  end
@@ -3,11 +3,12 @@
3
3
  module ::YeSQL
4
4
  module Config
5
5
  class Configuration
6
- attr_accessor :path
6
+ attr_accessor :connection, :path
7
7
 
8
- DEFAULT_PATH = 'app/yesql'
8
+ DEFAULT_PATH = "app/yesql"
9
9
 
10
10
  def initialize
11
+ @connection = ""
11
12
  @path = DEFAULT_PATH
12
13
  end
13
14
  end
@@ -1,28 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module YeSQL
3
+ module ::YeSQL
4
4
  module Errors
5
- module FilePathDoesNotExistError
6
- def validate_file_path_existence(file_path)
7
- return if file_exists?(file_path)
5
+ class FilePathDoesNotExistError
6
+ def initialize(file_path)
7
+ @file_path = file_path
8
+ end
9
+
10
+ def validate_file_path_existence
11
+ return if file_exists?
8
12
 
9
- raise NotImplementedError, format(MESSAGE, available_files: available_files,
10
- file_path: file_path, path: ::YeSQL.config.path)
13
+ raise ::NotImplementedError, message
11
14
  end
12
15
 
13
16
  private
14
17
 
15
- MESSAGE = <<~MSG
18
+ attr_reader :file_path
16
19
 
17
- SQL file "%<file_path>s" does not exist in %<path>s.
20
+ def message
21
+ "\nSQL file \"#{file_path}\" does not exist in #{::YeSQL.config.path}.
18
22
 
19
- Available SQL files are:
23
+ Available SQL files are:
20
24
 
21
- %<available_files>s
22
- MSG
23
- private_constant :MESSAGE
25
+ #{available_files}\n"
26
+ end
24
27
 
25
- def file_exists?(file_path)
28
+ def file_exists?
26
29
  path_files.any? { |filename| filename.include?("#{file_path}.sql") }
27
30
  end
28
31
 
@@ -1,41 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql/utils/read'
3
+ require "yesql/utils/read"
4
4
 
5
5
  module ::YeSQL
6
6
  module Errors
7
- module NoBindingsProvidedError
8
- def validate_statement_bindings(binds, file_path)
9
- return unless statement_binds(file_path).size.positive?
7
+ class NoBindingsProvidedError
8
+ include ::YeSQL::Utils::Read
10
9
 
11
- format(MESSAGE, renderable_statement_binds(file_path)).tap do |message|
12
- raise ::ArgumentError, message unless binds.is_a?(::Hash) && !binds.empty?
13
- end
10
+ def initialize(binds, file_path)
11
+ @binds = binds
12
+ @file_path = file_path
13
+ end
14
+
15
+ def validate_statement_bindings
16
+ return unless statement_binds.size.positive?
17
+ return if expected_binds?
18
+
19
+ raise ::ArgumentError, message
14
20
  end
15
21
 
16
22
  private
17
23
 
18
- MESSAGE = <<~MSG
24
+ attr_reader :binds, :file_path
19
25
 
20
- YeSQL invoked without bindings.
26
+ def message
27
+ "\nYeSQL invoked without bindings.
21
28
 
22
- Expected bindings are:
29
+ Expected bindings are:
23
30
 
24
- %s
25
- MSG
26
- private_constant :MESSAGE
31
+ #{renderable_statement_binds}\n"
32
+ end
27
33
 
28
- def statement_binds(file_path)
29
- ::YeSQL::Utils::Read.statement(file_path)
30
- .scan(::YeSQL::BIND_REGEX).tap do |scanned_binds|
31
- break [] if scanned_binds.size.zero?
34
+ def statement_binds
35
+ statement
36
+ .scan(::YeSQL::BIND_REGEX)
37
+ .tap { |scanned_binds| break (scanned_binds || []).sort }
38
+ end
32
39
 
33
- break scanned_binds.sort
34
- end
40
+ def renderable_statement_binds
41
+ statement_binds.flatten.map { |bind| "- `#{bind}`\n" }.join
35
42
  end
36
43
 
37
- def renderable_statement_binds(file_path)
38
- statement_binds(file_path).flatten.map { |bind| "- `#{bind}`\n" }.join
44
+ def expected_binds?
45
+ binds.is_a?(::Hash) && !binds.empty?
39
46
  end
40
47
  end
41
48
  end
@@ -1,22 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module YeSQL
3
+ module ::YeSQL
4
4
  module Errors
5
- module OutputArgumentError
6
- def validate_output_options(output)
5
+ class OutputArgumentError
6
+ def initialize(output)
7
+ @output = output
8
+ end
9
+
10
+ def validate_output_options
7
11
  return if output.nil?
12
+ return if OPTIONS.include?(output.to_sym)
8
13
 
9
- raise ArgumentError, format(MESSAGE, output) unless OPTIONS.include?(output.to_sym)
14
+ raise ArgumentError, message
10
15
  end
11
16
 
12
- MESSAGE = <<~MSG
13
- Unsupported `output` option given `%s`. Possible values are:
14
- - `columns`: returns an array with the columns from the result.
15
- - `hash`: returns an array of hashes combining both, the columns and rows from the statement result.
16
- - `rows`: returns an array of arrays for each row from the given SQL statement.
17
- MSG
17
+ private
18
+
19
+ attr_reader :output
20
+
18
21
  OPTIONS = %i[columns hash rows].freeze
19
- private_constant :MESSAGE, :OPTIONS
22
+ private_constant :OPTIONS
23
+
24
+ def message
25
+ "Unsupported `output` option given `#{output}`. Possible values are:
26
+ - `columns`: returns an array with the columns from the result.
27
+ - `hash`: returns an array of hashes combining both, the columns and rows from the statement result.
28
+ - `rows`: returns an array of arrays for each row from the given SQL statement.\n"
29
+ end
20
30
  end
21
31
  end
22
32
  end
@@ -1,27 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql'
4
- require 'forwardable'
3
+ require "yesql"
4
+ require "forwardable"
5
5
 
6
6
  module ::YeSQL
7
7
  module Params
8
8
  class Output
9
- extend Forwardable
9
+ extend ::Forwardable
10
10
 
11
11
  def initialize(output)
12
12
  @output = output.to_s
13
13
  end
14
14
 
15
15
  def columns?
16
- output == 'columns'
16
+ output == "columns"
17
17
  end
18
18
 
19
19
  def rows?
20
- output == 'rows'
20
+ output == "rows"
21
21
  end
22
22
 
23
23
  def hash?
24
- output == 'hash'
24
+ output == "hash"
25
25
  end
26
26
 
27
27
  def_delegator(:output, :to_sym)
@@ -1,58 +1,33 @@
1
1
  # frozen_string_literal: true
2
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
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
12
  module Query
13
13
  class Performer
14
14
  include ::YeSQL::Bindings::Utils
15
15
 
16
- # rubocop:disable Metrics/ParameterLists
17
- def initialize(bind_statement:,
18
- bindings: {},
19
- cache: {},
20
- file_path:,
21
- output: :rows,
22
- prepare: false)
16
+ def initialize(bind_statement:, file_path:, bindings: {}, output: :rows, prepare: false)
23
17
  @bind_statement = bind_statement
24
- @cache = cache
25
- @cache_key = cache[:key] || file_path
26
- @expires_in = cache[:expires_in]
27
18
  @file_path = file_path
28
19
  @named_bindings = bindings.transform_keys(&:to_sym)
29
20
  @output = ::YeSQL::Params::Output.new(output)
30
21
  @prepare = prepare
31
22
  end
32
- # rubocop:enable Metrics/ParameterLists
33
23
 
34
24
  def call
35
- return transformed_result if cache.empty?
36
-
37
- Rails.cache.fetch(cache_key, expires_in: expires_in) { transformed_result }
25
+ ::YeSQL::Query::TransformResult.new(output: output, result: query_result).call
38
26
  end
39
27
 
40
28
  private
41
29
 
42
- attr_reader :bind_statement,
43
- :cache,
44
- :cache_key,
45
- :expires_in,
46
- :file_path,
47
- :named_bindings,
48
- :output,
49
- :prepare,
50
- :rows
51
-
52
- def transformed_result
53
- @transformed_result ||=
54
- ::YeSQL::Query::TransformResult.new(output: output, result: query_result).call
55
- end
30
+ attr_reader :bind_statement, :file_path, :named_bindings, :output, :prepare
56
31
 
57
32
  def query_result
58
33
  @query_result ||= ::YeSQL::Query::Result.new(binds: binds,
@@ -1,37 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql'
4
- require 'forwardable'
5
- require 'yesql/common/adapter'
3
+ require "yesql"
4
+ require "forwardable"
5
+ require "yesql/common/adapter"
6
6
 
7
7
  module ::YeSQL
8
8
  module Query
9
9
  class Result
10
- extend Forwardable
10
+ extend ::Forwardable
11
11
 
12
12
  include ::YeSQL::Common::Adapter
13
13
 
14
- def initialize(binds: [], bind_statement:, file_path:, prepare:)
14
+ def initialize(bind_statement:, file_path:, prepare:, binds: [])
15
15
  @binds = binds
16
16
  @bind_statement = bind_statement
17
- @connection = ActiveRecord::Base.connection
18
17
  @file_path = file_path
19
18
  @prepare_option = prepare
20
19
  end
21
20
 
22
21
  def call
23
- return view_result if view?
24
- return rails5_result if ::Rails::VERSION::MAJOR == 5 && mysql?
22
+ return view_result if view?
23
+ return rails5_result if ::ActiveRecord::VERSION::MAJOR == 5 && mysql?
25
24
 
26
25
  exec_query(bound, file_path, binds, prepare: prepare_option)
27
26
  end
28
27
 
29
28
  private
30
29
 
31
- attr_reader :binds, :bind_statement, :connection, :file_path, :prepare_option
30
+ attr_reader :binds, :bind_statement, :file_path, :prepare_option
32
31
 
33
32
  def_delegators(:bind_statement, :bound, :to_s, :view?)
34
- def_delegators(:connection, :exec_query, :raw_connection)
33
+ def_delegators(:connection, :exec_query, :raw_connection)
35
34
  def_delegators(:raw_connection, :prepare)
36
35
 
37
36
  def view_result
@@ -41,6 +40,10 @@ module ::YeSQL
41
40
  def rails5_result
42
41
  prepare(bound).execute(*binds)
43
42
  end
43
+
44
+ def connection
45
+ @connection ||= ActiveRecord::Base.connection
46
+ end
44
47
  end
45
48
  end
46
49
  end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yesql'
4
- require 'yesql/common/adapter'
5
- require 'forwardable'
3
+ require "yesql"
4
+ require "yesql/common/adapter"
5
+ require "forwardable"
6
6
 
7
7
  module ::YeSQL
8
8
  module Query
9
9
  class TransformResult
10
- extend Forwardable
10
+ extend ::Forwardable
11
11
 
12
12
  include ::YeSQL::Common::Adapter
13
13
 
@@ -17,7 +17,7 @@ module ::YeSQL
17
17
  end
18
18
 
19
19
  def call
20
- if ::Rails::VERSION::MAJOR == 5 && mysql?
20
+ if rails_5? && mysql?
21
21
  return columns if columns?
22
22
  return rows_values if rows?
23
23
  return array_of_symbol_hashes if hash?
@@ -32,7 +32,7 @@ module ::YeSQL
32
32
 
33
33
  attr_reader :output, :result
34
34
 
35
- def_delegators(:result, :rows, :to_a)
35
+ def_delegators(:result, :fields, :rows, :to_a)
36
36
  def_delegators(:output, :columns?, :hash?, :rows?)
37
37
 
38
38
  def rows_values
@@ -40,8 +40,9 @@ module ::YeSQL
40
40
  end
41
41
 
42
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 }
43
+ to_a
44
+ .tap { |rows| break hashed_rows(rows) if ::ActiveRecord::VERSION::MAJOR == 5 }
45
+ .map { |e| e.respond_to?(:symbolize_keys) ? e.symbolize_keys : e }
45
46
  end
46
47
 
47
48
  def hashed_rows(rows)
@@ -49,9 +50,11 @@ module ::YeSQL
49
50
  end
50
51
 
51
52
  def columns
52
- return result.fields if result.respond_to?(:fields)
53
+ fields || result.columns
54
+ end
53
55
 
54
- result.columns
56
+ def rails_5?
57
+ ::ActiveRecord::VERSION::MAJOR == 5
55
58
  end
56
59
  end
57
60
  end
@@ -1,35 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'forwardable'
3
+ require "active_record"
4
4
 
5
- require 'yesql/utils/read'
6
- require 'yesql/bindings/extractor'
7
- require 'yesql/common/adapter'
5
+ require "forwardable"
6
+
7
+ require "yesql/utils/read"
8
+ require "yesql/bindings/extractor"
9
+ require "yesql/common/adapter"
8
10
 
9
11
  module ::YeSQL
10
12
  class Statement
11
- extend Forwardable
13
+ extend ::Forwardable
12
14
 
13
15
  include ::YeSQL::Common::Adapter
16
+ include ::YeSQL::Utils::Read
17
+ # Give access to the quote method.
18
+ include ::ActiveRecord::ConnectionAdapters::Quoting
14
19
 
15
20
  def initialize(bindings = {}, file_path)
16
21
  @bindings = bindings
17
- @connection = ::ActiveRecord::Base.connection
18
22
  @file_path = file_path
19
23
  end
20
24
 
21
25
  def bound
22
26
  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
27
+ extract_bind_values(extractor[match[/(\w+)/].to_sym])
28
28
  end
29
29
  end
30
30
 
31
31
  def to_s
32
- @to_s ||= ::YeSQL::Utils::Read.statement(file_path, readable: true)
32
+ @to_s ||= statement(readable: true)
33
33
  end
34
34
 
35
35
  def view?
@@ -38,9 +38,13 @@ module ::YeSQL
38
38
 
39
39
  private
40
40
 
41
- def_delegator(:connection, :quote)
41
+ attr_reader :bindings, :file_path
42
42
 
43
- attr_reader :bindings, :connection, :file_path
43
+ def extract_bind_values(match)
44
+ return quote(match[:value]) if view?
45
+
46
+ match[:bind][:vars]
47
+ end
44
48
 
45
49
  def extractor
46
50
  ::YeSQL::Bindings::Extractor.new(bindings: bindings).call
@@ -1,16 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module YeSQL
3
+ module ::YeSQL
4
4
  module Utils
5
5
  module Read
6
- def self.statement(file_path, readable: false)
7
- Dir["./#{::YeSQL.config.path}/**/*.sql"]
8
- .find { |dir_file_path| dir_file_path.include?("#{file_path}.sql") }
9
- .tap do |sql_file_path|
10
- break File.readlines(sql_file_path, chomp: true).join(' ') if readable == true
6
+ def statement(readable: false)
7
+ read_file(found_file, readable)
8
+ end
9
+
10
+ private
11
11
 
12
- break File.read(sql_file_path)
13
- end
12
+ def read_file(file, readable)
13
+ return File.readlines(file, chomp: true).join(" ") if readable == true
14
+
15
+ File.read(file)
16
+ end
17
+
18
+ def found_file
19
+ dir_sql_files.find { |dir_file_path| dir_file_path.include?("#{file_path}.sql") }
20
+ end
21
+
22
+ def dir_sql_files
23
+ Dir["./#{::YeSQL.config.path}/**/*.sql"]
14
24
  end
15
25
  end
16
26
  end
data/lib/yesql/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module YeSQL
4
- VERSION = '0.1.7'
3
+ module ::YeSQL
4
+ VERSION = "0.2.3"
5
5
  end
data/yesql.gemspec CHANGED
@@ -1,28 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'lib/yesql/version'
3
+ require_relative "lib/yesql/version"
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = 'yesql'
6
+ spec.name = "yesql"
7
7
  spec.version = YeSQL::VERSION
8
- spec.authors = ['Sebastián Palma']
9
- spec.email = ['vnhnhm.github@gmail.com']
10
- spec.summary = 'Ruby library to use SQL'
8
+ spec.authors = ["Sebastián Palma"]
9
+ spec.email = ["vnhnhm.github@gmail.com"]
10
+ spec.summary = "Ruby library to use SQL"
11
11
  spec.description = 'SQL "raw" for Rails projects'
12
- spec.homepage = 'https://github.com/sebastian-palma/yesql'
13
- spec.license = 'MIT'
14
- spec.metadata['homepage_uri'] = spec.homepage
15
- spec.metadata['source_code_uri'] = 'https://github.com/sebastian-palma/yesql'
16
- spec.metadata['changelog_uri'] = 'https://github.com/sebastian-palma/yesql'
12
+ spec.homepage = "https://github.com/sebastian-palma/yesql"
13
+ spec.license = "MIT"
14
+ spec.metadata["homepage_uri"] = spec.homepage
15
+ spec.metadata["source_code_uri"] = "https://github.com/sebastian-palma/yesql"
16
+ spec.metadata["changelog_uri"] = "https://github.com/sebastian-palma/yesql"
17
17
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
18
18
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
19
  end
20
- spec.bindir = 'exe'
20
+ spec.bindir = "exe"
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
- spec.require_paths = ['lib']
23
- spec.add_dependency 'rails', '>= 5.0'
24
- spec.add_development_dependency 'mysql2', '~> 0.5.3'
25
- spec.add_development_dependency 'pg', '>= 0.18'
26
- spec.add_development_dependency 'pry', '~> 0.13.1'
27
- spec.add_development_dependency 'rspec', '~> 3.9.0'
22
+ spec.require_paths = ["lib"]
23
+ spec.add_dependency "activerecord", ">= 4.0.0.beta1"
24
+ spec.add_development_dependency "mysql2", "~> 0.5.3"
25
+ spec.add_development_dependency "pg", ">= 0.18"
26
+ spec.add_development_dependency "pry", "~> 0.13.1"
27
+ spec.add_development_dependency "rspec", "~> 3.9.0"
28
28
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yesql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastián Palma
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-01 00:00:00.000000000 Z
11
+ date: 2021-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: 4.0.0.beta1
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: '5.0'
26
+ version: 4.0.0.beta1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: mysql2
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -103,7 +103,6 @@ files:
103
103
  - lib/yesql/bindings/utils.rb
104
104
  - lib/yesql/common/adapter.rb
105
105
  - lib/yesql/config/configuration.rb
106
- - lib/yesql/errors/cache_expiration_error.rb
107
106
  - lib/yesql/errors/file_path_does_not_exist_error.rb
108
107
  - lib/yesql/errors/no_bindings_provided_error.rb
109
108
  - lib/yesql/errors/output_argument_error.rb
@@ -122,7 +121,7 @@ metadata:
122
121
  homepage_uri: https://github.com/sebastian-palma/yesql
123
122
  source_code_uri: https://github.com/sebastian-palma/yesql
124
123
  changelog_uri: https://github.com/sebastian-palma/yesql
125
- post_install_message:
124
+ post_install_message:
126
125
  rdoc_options: []
127
126
  require_paths:
128
127
  - lib
@@ -137,8 +136,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
136
  - !ruby/object:Gem::Version
138
137
  version: '0'
139
138
  requirements: []
140
- rubygems_version: 3.1.2
141
- signing_key:
139
+ rubygems_version: 3.2.3
140
+ signing_key:
142
141
  specification_version: 4
143
142
  summary: Ruby library to use SQL
144
143
  test_files: []
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module YeSQL
4
- module Errors
5
- module CacheExpirationError
6
- def validate_cache_expiration(expires_in)
7
- raise ArgumentError, MESSAGE unless expires_in.is_a?(ActiveSupport::Duration)
8
- end
9
-
10
- MESSAGE = <<~MSG
11
- Missing mandatory `expires_in` option for `cache`.
12
-
13
- Can not cache the result of the query without an expiration date.
14
- MSG
15
- private_constant :MESSAGE
16
- end
17
- end
18
- end