yesql 0.1.7 → 0.2.3

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 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