quote-sql 0.0.1 → 0.0.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: 13f64b3b81c62230c2814c487d3a5b2e56109b651297a599449a2b8edd91b3d8
4
- data.tar.gz: 8fc4b373eb65ab1d6ba2e86a32db5d60caf3bef38096e9a098127d9f892a4abe
3
+ metadata.gz: 17336c46db1b966512f67b1c4dae714d612460ae61c1f24d75d0fa79153422df
4
+ data.tar.gz: 8b510daed8f21c7733e0b841f0c11f60c8634abde062e3a81dc844b8ed4cb682
5
5
  SHA512:
6
- metadata.gz: d58cc18c35110eef2ddb9c08f1e23f9b353ab7fc73939063fa3a576156c0fd96b05099e8fe8742fcb4c46261735fc47d547d24f6218ce9f6776c8e91be873211
7
- data.tar.gz: 7facea8c215baf9c9196512b636b3ae866343eea0ca8137589b25f3cd6684a4fa815661c05059be58c11ace2c0fc6ac3962d96d02c3c9be1fdef628a00c6349d
6
+ metadata.gz: 1cc8dd6b2e36c5f5e976027d1f25442cf3cbee76b8bf5bf05b827ba2693d2aed952273491b1abd6404f53482b6da1c0e88e5de5cc9d25ac3af56b65f649f0aa7
7
+ data.tar.gz: 3e4a135613d5c22963030754e1bc21d8ac3d3aaf210abecdd2d8eeb47893f96c3243702c29d138321a122c28ed571670e32c0f703462cf1a1ca41e6b925f6da5
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # QuoteSql - Tool to build and run SQL queries easier
2
2
  I've built this library as an addition to ActiveRecord and Arel, however you can use it with any sql database and plain Ruby.
3
+ However currently it is just used with PostgreSQL.
3
4
 
4
5
  Creating SQL queries and proper quoting becomes complicated especially when you need advanced queries.
5
6
 
@@ -8,7 +9,7 @@ I created this library while coding for different projects, and had lots of Here
8
9
 
9
10
  My strategy is to segment SQL Queries in readable junks, which can be individually tested and then combine their sql to the final query.
10
11
 
11
- QuoteSql is used in production, but is still evolving.
12
+ QuoteSql is used in production, but is still bleeding edge - and there is not a fully sync between doc and code.
12
13
 
13
14
  If you think QuoteSql is interesting, let's chat!
14
15
  Also if you have problems using it, just drop me a note.
@@ -23,6 +24,10 @@ Best Martin
23
24
  `QuoteSql.new("SELECT %field__text").quote(field__text: 9).to_sql`
24
25
  => SELECT 9::TEXT
25
26
 
27
+ ### Rails models
28
+ `QuoteSql.new(Users.limit(10).select("%columns")).quote(columns: ['first_name', 'last_name').to_sql`
29
+ => SELECT first_name, last_name FROM users LIMIT 10
30
+
26
31
  ### Quoting of columns and table from a model - or an object responding to table_name and column_names or columns
27
32
  `QuoteSql.new("SELECT %columns FROM %table_name").quote(table: User).to_sql`
28
33
  => SELECT "id",firstname","lastname",... FROM "users"
@@ -47,10 +52,18 @@ Values are be ordered in sequence of columns. Missing value entries are substitu
47
52
  ON CONFLICT ("id") DO NOTHING
48
53
 
49
54
  ### Columns from a list
50
- `QuoteSql.new("SELECT %columns").quote(columns: [:a, :"b.c", c: "jsonb_build_object('d', 1)"]).to_sql`
51
- => SELECT "a","b"."c",jsonb_build_object('d', 1) AS c
55
+ `QuoteSql.new("SELECT %columns").quote(columns: [:a, :"b.c", c: {d: field}]).to_sql`
56
+ => SELECT "a","b"."c",jsonb_build_object('d', field) AS c
57
+
58
+ `QuoteSql.new("SELECT %columns").quote(columns: [:a, :"b.c", c: {d: field, nil: false}]).to_sql`
59
+ => SELECT "a","b"."c",jsonb_strip_nulls(jsonb_build_object('d', 1)) AS c
60
+
61
+
62
+ ### Execution of a query
63
+ `QuoteSql.new("Select 1 as abc").result` => [{:abc=>1}]
52
64
 
53
- ## Substitution of
65
+
66
+ ## Substitution of mixins with quoted values
54
67
  In the SQL matches of `%foo` or `%{foo}` or `%foo_4_bar` or `%{foo_4_bar}` the *"mixins"*
55
68
  are substituted with quoted values
56
69
  the values are looked up from the options given in the quotes method
@@ -106,6 +119,16 @@ with optional array dimension
106
119
  - +String+ value will become the expression, the key the AS {result: "SUM(*)"} => SUM(*) AS result
107
120
  - +Proc+ are executed with the +QuoteSQL::Quoter+ object as parameter and added as raw SQL
108
121
 
122
+ ## Shortcuts and functions
123
+ - `QuoteSQL("select %abc", abc: 1)` == `QuoteSql.new("select %abc").quote(abc: 1)`
124
+ - when you have in your initializer `String.include QuoteSql::Extension` you can do e.g. `"select %abc".quote_sql(abc: 1)`
125
+ - when you have in your initializer `ActiveRecord::Relation.include QuoteSql::Extension` you can do e.g. `Profile.limit(10).select('%abc').quote_sql(abc: 1)`
126
+
127
+ ## Debug and dump
128
+ If you have pg_format installed you can get the resulting query inspected:
129
+ `QuoteSql.new("select %abc").quote(abc: 1).dsql`
130
+
131
+
109
132
  ## Installing
110
133
  `gem install quote-sql`
111
134
  or in Gemfile
@@ -121,3 +144,10 @@ Add this to config/initializers/quote_sql.rb
121
144
  ActiveRecord::Relation.include QuoteSql::Extension
122
145
  end
123
146
 
147
+ ## Todos
148
+ - Functionalities not yet used in my production might not work
149
+ - More documentation
150
+ - Tests missing
151
+ - Missing functionalities
152
+ - Prepare
153
+ - which other - let me know!
@@ -5,15 +5,23 @@ class QuoteSql
5
5
  @key, @quotable = key, quotable
6
6
  end
7
7
 
8
+ def quotes
9
+ @qsql.quotes
10
+ end
11
+
8
12
  attr_reader :key, :quotable
9
13
 
14
+ def name
15
+ @key.sub(/_[^_]+$/, '')
16
+ end
17
+
10
18
  def to_sql
11
19
  return @quotable.call(self) if @quotable.is_a? Proc
12
20
  case key.to_s
13
21
  when /(?:^|(.*)_)table$/i
14
22
  table
15
23
  when /(?:^|(.*)_)columns?$/i
16
- column_names
24
+ columns
17
25
  when /(?:^|(.*)_)(table_name?s?)$/i
18
26
  table_name
19
27
  when /(?:^|(.*)_)(column_name?s?)$/i
@@ -22,10 +30,12 @@ class QuoteSql
22
30
  ident_name
23
31
  when /(?:^|(.*)_)constraints?$/i
24
32
  quotable.to_s
25
- when /(?:^|(.*)_)(raw|sql)$/
33
+ when /(?:^|(.*)_)(raw|sql)$/i
26
34
  quotable.to_s
27
- when /(?:^|(.*)_)(values?)$/
28
- values
35
+ when /^(.+)_values$/i
36
+ data_values
37
+ when /values$/i
38
+ insert_values
29
39
  else
30
40
  quote
31
41
  end
@@ -56,24 +66,75 @@ class QuoteSql
56
66
 
57
67
  end
58
68
 
59
- def values(item = @quotable)
69
+ def data_values(item = @quotable)
70
+ item = Array(item).compact
71
+ column_names = @qsql.quotes[:"#{name}_columns"].dup
72
+ if column_names.is_a? Hash
73
+ types = column_names.values.map { "::#{_1.upcase}" if _1 }
74
+ column_names = column_names.keys
75
+ end
76
+ if item.all? { _1.is_a?(Hash) }
77
+ column_names ||= item.flat_map { _1.keys.sort }.uniq
78
+ item.map! { _1.fetch_values(*column_names) {} }
79
+ end
80
+ if item.all? { _1.is_a?(Array) }
81
+ length, overflow = item.map { _1.length }.uniq
82
+ raise ArgumentError, "all values need to have the same length" if overflow
83
+ column_names ||= (1..length).map{"column#{_1}"}
84
+ raise ArgumentError, "#{name}_columns and value lengths need to be the same" if column_names.length != length
85
+ values = item.map { value(_1) }
86
+ else
87
+ raise ArgumentError, "Either all type Hash or Array"
88
+ end
89
+ if types.present?
90
+ value = values[0][1..-2].split(/\s*,\s*/)
91
+ types.each_with_index { value[_2] << _1 || ""}
92
+ values[0] = "(" + value.join(",") + ")"
93
+ end
94
+ # values[0] { _1 << types[_1] || ""}
95
+ "(VALUES #{values.join(",")}) AS #{ident_name name} (#{ident_name column_names})"
96
+ end
97
+
98
+
99
+ def insert_values(item = @quotable)
60
100
  case item
61
101
  when Arel::Nodes::SqlLiteral
62
102
  item = Arel.sql("(#{item})") unless item[/^\s*\(/] and item[/\)\s*$/]
63
103
  return item
64
104
  when Array
105
+ item.compact!
106
+ column_names = (@qsql.quotes[:columns] || @qsql.quotes[:column_names]).dup
107
+ types = []
108
+ if column_names.is_a? Hash
109
+ types = column_names.values.map { "::#{_1.upcase}" if _1 }
110
+ column_names = column_names.keys
111
+ elsif column_names.is_a? Array
112
+ column_names = column_names.map do |column|
113
+ types << column.respond_to?(:sql_type) ? "::#{column.sql_type}" : nil
114
+ column.respond_to?(:name) ? column.name : column
115
+ end
116
+ end
117
+
118
+ if item.all? { _1.is_a?(Hash) }
119
+ column_names ||= item.flat_map { _1.keys.sort }.uniq
120
+ item.map! { _1.fetch_values(*column_names) {} }
121
+ end
65
122
 
66
- differences = item.map { _1.is_a?(Array) && _1.length }.uniq
67
- if differences.length == 1
68
- item.compact.map { value(_1) }.join(", ")
123
+ if item.all? { _1.is_a?(Array) }
124
+ length, overflow = item.map { _1.length }.uniq
125
+ raise ArgumentError, "all values need to have the same length" if overflow
126
+ raise ArgumentError, "#{name}_columns and value lengths need to be the same" if column_names and column_names.length != length
127
+ values = item.map { value(_1) }
128
+ else
129
+ raise ArgumentError, "Either all type Hash or Array"
130
+ end
131
+ if column_names.present?
132
+ "(#{ident_name column_names}) VALUES #{values.join(",")}"
69
133
  else
70
- value([item])
134
+ "VALUES #{values.join(",")}"
71
135
  end
72
136
  when Hash
73
137
  value([item])
74
- else
75
- return item.to_sql if item.respond_to? :to_sql
76
- "(" + _quote(item) + ")"
77
138
  end
78
139
  end
79
140
 
@@ -121,6 +182,20 @@ class QuoteSql
121
182
  end
122
183
  end
123
184
 
185
+ def columns(item = @quotable)
186
+ if item.respond_to?(:column_names)
187
+ item = item.column_names
188
+ elsif item.class.respond_to?(:column_names)
189
+ item = item.class.column_names
190
+ elsif item.is_a?(Array)
191
+ if item.all?{ _1.respond_to?(:name) }
192
+ item = item.map(&:name)
193
+ end
194
+ end
195
+ @qsql.column_names ||= item
196
+ ident_name(item)
197
+ end
198
+
124
199
  def column_names(item = @quotable)
125
200
  if item.respond_to?(:column_names)
126
201
  item = item.column_names
@@ -133,6 +208,13 @@ class QuoteSql
133
208
  ident_name(item)
134
209
  end
135
210
 
211
+ def json_build_object(h)
212
+ compact = h.delete(nil) == false
213
+ rv = "jsonb_build_object(" + h.map { "'#{_1}',#{_2}" }.join(",") + ")"
214
+ return rv unless compact
215
+ "jsonb_strip_nulls(#{rv})"
216
+ end
217
+
136
218
  def ident_name(item = @quotable)
137
219
  case item
138
220
  when Array
@@ -147,14 +229,18 @@ class QuoteSql
147
229
  end
148
230
  end.join(",")
149
231
  when Hash
150
- item.map do
151
- case _2
232
+ item.map do |k,v|
233
+ case v
152
234
  when Symbol
153
- _quote_column_name(_1, _2)
235
+ _quote_column_name(k, v)
154
236
  when String
155
- "#{_2} AS #{_1}"
237
+ "#{v} AS #{k}"
156
238
  when Proc
157
239
  item.call(self)
240
+ when Hash
241
+ "#{json_build_object(v)} AS #{k}"
242
+ else
243
+ raise ArgumentError
158
244
  end
159
245
  end.join(",")
160
246
  else
@@ -1,21 +1,28 @@
1
1
  module QuoteSql::Test
2
2
  def self.all
3
+ @success = []
4
+ @fail = []
3
5
  methods(false).grep(/^test_/).each do |name|
4
- run(name)
5
- puts
6
+ run(name, true)
6
7
  end
7
-
8
+ @success.each { STDOUT.puts(*_1, nil) }
9
+ @fail.each { STDOUT.puts(*_1, nil) }
10
+ puts
8
11
  end
9
12
 
10
- def self.run(name)
13
+ def self.run(name, all)
11
14
  name = name.to_s.sub(/^test_/, "")
12
15
  @expected = nil
13
16
  @test = send("test_#{name}")
14
- if sql.gsub(/\s+/, "") == expected&.gsub(/\s+/, "")
15
- STDOUT.puts name, @test.original, @test.quotes.inspect, " #{expected}"
17
+
18
+ if sql.gsub(/\s+/, "")&.downcase&.strip == expected&.gsub(/\s+/, "")&.downcase&.strip
19
+ rv = [name, @test.original, @test.quotes.inspect, "✅ #{expected}"]
20
+ @success << rv if @success
16
21
  else
17
- STDOUT.puts name, @test.inspect, sql, "❌ #{expected}"
22
+ rv = [name, @test.inspect, sql, "❌ #{expected}"]
23
+ @fail << rv if @fail
18
24
  end
25
+ STDOUT.puts rv unless @fail or @success
19
26
  end
20
27
 
21
28
  def self.expected(v = nil)
@@ -42,7 +49,7 @@ module QuoteSql::Test
42
49
 
43
50
  class << self
44
51
  def test_columns_and_table_name_simple
45
- expected Arel.sql(%(SELECT "a","b"."c" FROM "my_table"))
52
+ expected %(SELECT "a","b"."c" FROM "my_table")
46
53
  QuoteSql.new("SELECT %columns FROM %table_name").quote(
47
54
  columns: [:a, b: :c],
48
55
  table_name: "my_table"
@@ -50,7 +57,7 @@ module QuoteSql::Test
50
57
  end
51
58
 
52
59
  def test_columns_and_table_name_complex
53
- expected Arel.sql(%(SELECT "a","b"."c" FROM "table1","table2"))
60
+ expected %(SELECT "a","b"."c" FROM "table1","table2")
54
61
  QuoteSql.new("SELECT %columns FROM %table_names").quote(
55
62
  columns: [:a, b: :c],
56
63
  table_names: ["table1", "table2"]
@@ -58,7 +65,7 @@ module QuoteSql::Test
58
65
  end
59
66
 
60
67
  def test_recursive_injects
61
- expected Arel.sql(%(SELECT TRUE FROM "table1"))
68
+ expected %(SELECT TRUE FROM "table1")
62
69
  QuoteSql.new("SELECT %raw FROM %table_names").quote(
63
70
  raw: "%recurse1_raw",
64
71
  recurse1_raw: "%recurse2",
@@ -68,7 +75,9 @@ module QuoteSql::Test
68
75
  end
69
76
 
70
77
  def test_values
71
- expected Arel.sql(%(SELECT 'a text', 123, 'text' AS abc FROM "my_table"))
78
+ expected <<~SQL
79
+ SELECT 'a text', 123, 'text' AS abc FROM "my_table"
80
+ SQL
72
81
  QuoteSql.new("SELECT %text, %{number}, %aliased_with_hash FROM %table_name").quote(
73
82
  text: "a text",
74
83
  number: 123,
@@ -80,33 +89,92 @@ module QuoteSql::Test
80
89
  end
81
90
 
82
91
  def test_binds
83
- expected Arel.sql(%(SELECT $1, $2, $1 AS get_bind_1_again FROM "my_table"))
84
- QuoteSql.new("SELECT %bind, %bind__uuid, %bind1 AS get_bind_1_again FROM %table_name").quote(
85
- table_name: "my_table"
86
- )
92
+ expected <<~SQL
93
+ SELECT $1, $2, $1 AS get_bind_1_again FROM "my_table"
94
+ SQL
95
+ QuoteSql.new("SELECT %bind, %bind__uuid, %bind1 AS get_bind_1_again FROM %table_name").quote(
96
+ table_name: "my_table"
97
+ )
98
+ end
99
+
100
+ def test_from_values_array
101
+ expected <<~SQL
102
+ SELECT * FROM (VALUES ('a',1,TRUE,NULL)) AS "x" ("column1","column2","column3","column4")
103
+ SQL
104
+ "SELECT * FROM %x_values".quote_sql(x_values: [['a', 1, true, nil]])
105
+ end
106
+
107
+ def test_from_values_hash_no_columns
108
+ expected <<~SQL
109
+ SELECT * FROM (VALUES ('a', 1, true, NULL), ('a', 1, true, NULL), (NULL, 1, NULL, 2)) AS "y" ("a", "b", "c", "d")
110
+ SQL
111
+ "SELECT * FROM %y_values".quote_sql(y_values: [
112
+ { a: 'a', b: 1, c: true, d: nil },
113
+ { d: nil, a: 'a', c: true, b: 1 },
114
+ { d: 2, b: 1 }
115
+ ])
87
116
  end
88
117
 
89
- def test_q3
90
- expected Arel.sql(<<-SQL)
91
- INSERT INTO "responses" ("id","type","task_id","index","data","parts","value","created_at","updated_at")
92
- VALUES (NULL,TRUE,'A','[5,5]','{"a":1}'),
93
- (1,FALSE,'B','[]','{"a":2}'),
94
- (2,NULL,'c','[1,2,3]','{"a":3}')
95
- ON CONFLICT (responses_task_id_index_unique) DO NOTHING;
96
- SQL
97
-
98
- QuoteSql.new(<<-SQL).
99
- INSERT INTO %table (%columns) VALUES %values
100
- ON CONFLICT (responses_task_id_index_unique) DO NOTHING;
101
- SQL
102
- quote(
103
- table: Response,
104
- values: [
105
- [nil, true, "A", [5, 5], { a: 1 }],
106
- [1, false, "B", [], { a: 2 }],
107
- [2, nil, "c", [1, 2, 3], { a: 3 }]
108
- ]
109
- )
118
+ def test_from_values_hash_with_columns
119
+ expected <<~SQL
120
+ SELECT * FROM (VALUES (NULL, true, 1, 'a')) AS "x" ("d","c","b","a")
121
+ SQL
122
+ "SELECT * FROM %x_values".quote_sql(x_columns: %i[d c b a], x_values: [{ a: 'a', b: 1, c: true, d: nil }])
110
123
  end
124
+
125
+ def test_from_values_hash_with_type_columns
126
+ expected <<~SQL
127
+ SELECT * FROM (VALUES ('a'::TEXT, 1::INTEGER, true::BOOLEAN, NULL::FLOAT), ('a', 1, true, NULL), (NULL, 1, NULL, 2)) AS "x" ("a", "b", "c", "d")
128
+ SQL
129
+ "SELECT * FROM %x_values".quote_sql(
130
+ x_columns: {
131
+ a: "text",
132
+ b: "integer",
133
+ c: "boolean",
134
+ d: "float"
135
+ },
136
+ x_values: [
137
+ { a: 'a', b: 1, c: true, d: nil },
138
+ { d: nil, a: 'a', c: true, b: 1 },
139
+ { d: 2, b: 1 }
140
+ ])
141
+ end
142
+
143
+ def test_insert_values_array
144
+ expected <<~SQL
145
+ INSERT INTO x VALUES ('a', 1, true, NULL)
146
+ SQL
147
+ "INSERT INTO x %values".quote_sql(values: [['a', 1, true, nil]])
148
+ end
149
+
150
+ def test_insert_values_hash
151
+ expected <<~SQL
152
+ INSERT INTO x ("a", "b", "c", "d") VALUES ('a', 1, true, NULL)
153
+ SQL
154
+ "INSERT INTO x %values".quote_sql(values: [{ a: 'a', b: 1, c: true, d: nil }])
155
+ end
156
+
157
+ # def test_q3
158
+ # expected Arel.sql(<<-SQL)
159
+ # INSERT INTO "responses" ("id","type","task_id","index","data","parts","value","created_at","updated_at")
160
+ # VALUES (NULL,TRUE,'A','[5,5]','{"a":1}'),
161
+ # (1,FALSE,'B','[]','{"a":2}'),
162
+ # (2,NULL,'c','[1,2,3]','{"a":3}')
163
+ # ON CONFLICT (responses_task_id_index_unique) DO NOTHING;
164
+ # SQL
165
+ #
166
+ # QuoteSql.new(<<-SQL).
167
+ # INSERT INTO %table (%columns) VALUES %values
168
+ # ON CONFLICT (responses_task_id_index_unique) DO NOTHING;
169
+ # SQL
170
+ # quote(
171
+ # table: Response,
172
+ # values: [
173
+ # [nil, true, "A", [5, 5], { a: 1 }],
174
+ # [1, false, "B", [], { a: 2 }],
175
+ # [2, nil, "c", [1, 2, 3], { a: 3 }]
176
+ # ]
177
+ # )
178
+ # end
111
179
  end
112
180
  end
data/lib/quote_sql.rb CHANGED
@@ -1,4 +1,4 @@
1
- Dir.glob(__FILE__.sub(/\.rb$/, "/*.rb")).each { require(_1) unless _1[/test\.rb$/] }
1
+ Dir.glob(__FILE__.sub(/\.rb$/, "/*.rb")).each { require(_1) unless _1[/(deprecated|test)\.rb$/] }
2
2
 
3
3
  # Tool to build and run SQL queries easier
4
4
  #
@@ -151,18 +151,29 @@ time(stamp)?(_\\(\d+\\))?(_with(out)?_time_zone)?
151
151
  end
152
152
 
153
153
  class Error < ::RuntimeError
154
- def initialize(quote_sql)
154
+ def initialize(quote_sql, errors)
155
155
  @object = quote_sql
156
+ @errors = errors
156
157
  end
157
158
 
159
+ attr_reader :object, :errors
160
+
161
+ def sql
162
+ @object.original.inspect
163
+ end
164
+
165
+ # def inspect
166
+ # super + errors.flat_map { [_1.inspect, _1.backtrace] }
167
+ # end
168
+
158
169
  def message
159
- super + %Q@<QuoteSql #{@object.original.inspect} #{@object.errors.inspect}>@
170
+ super + %Q@<QuoteSql #{sql} #{@object.errors.inspect}>@
160
171
  end
161
172
  end
162
173
 
163
174
  def to_sql
164
175
  mixin!
165
- raise Error.new(self) if errors?
176
+ raise Error.new(self, errors) if errors?
166
177
  return Arel.sql @sql if defined? Arel
167
178
  @sql
168
179
  end
@@ -224,8 +235,8 @@ time(stamp)?(_\\(\d+\\))?(_with(out)?_time_zone)?
224
235
  def errors
225
236
  @quotes.to_h do |k, v|
226
237
  r = @resolved[k]
227
- next [nil, nil] unless r.nil? or r.is_a?(Exception)
228
- [k, "#{@quotes[k].inspect} => #{v.inspect}"]
238
+ next [nil, nil] if r.nil? or not r.is_a?(Exception)
239
+ [k, {@quotes[k].inspect => v.inspect, exc: r, backtrace: r.backtrace}]
229
240
  end.compact
230
241
  end
231
242
 
@@ -294,6 +305,15 @@ time(stamp)?(_\\(\d+\\))?(_with(out)?_time_zone)?
294
305
 
295
306
  extend Quoting
296
307
 
308
+ def self.test
309
+ require __dir__ + "/quote_sql/test.rb"
310
+ Test
311
+ end
312
+ end
313
+
314
+ def QuoteSQL(sql, **options)
315
+ rv = QuoteSql.new(sql)
316
+ options.any? ? rv.quote(**options) : rv
297
317
  end
298
318
 
299
319
  QuoteSql.include QuoteSql::Formater
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quote-sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Kufner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-23 00:00:00.000000000 Z
11
+ date: 2024-02-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'QuoteSql helps you creating SQL queries and proper quoting especially
14
14
  with advanced queries.
@@ -39,9 +39,9 @@ require_paths:
39
39
  - lib
40
40
  required_ruby_version: !ruby/object:Gem::Requirement
41
41
  requirements:
42
- - - ">="
42
+ - - "~>"
43
43
  - !ruby/object:Gem::Version
44
- version: '0'
44
+ version: '3.0'
45
45
  required_rubygems_version: !ruby/object:Gem::Requirement
46
46
  requirements:
47
47
  - - ">="