niceql 0.2.0 → 0.3.0

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: 7f9f1dc89c0c7353d4b8c63056d43968b0ad2f71a602d4e50ba5a48bfa568ed0
4
- data.tar.gz: 9fc1ea32fc0ed3053702b3014dbb270457af028e61b6b1bb63ff6c28005d8ce4
3
+ metadata.gz: d33cb207d137e2c0e3b3fe59c2b9d525878a6def4a0be7b178b3555c98417148
4
+ data.tar.gz: c02577dea05cb51e74f1478be10241c7e6ef5a4554a92ef38da85b706fb11737
5
5
  SHA512:
6
- metadata.gz: e0b76948b56fe949aac0b5582081d42a60e35dede01332772c4fb233bd8c14bd2cf1d029187d25e0bb85c20d660f29763d401f2283695f28da36c0053293411c
7
- data.tar.gz: 2be8cfca05e187114624a2c70b9318c2dd161c42aca5c3ad90456e2ea2a46cda59de1aadd4d22e8e1111155078fa03b986ce4e7624e809776934eb0c0c94dcb5
6
+ metadata.gz: 7e03b3cbb34a26b1bc246b3aba84a775fca4cd0aaac2c87ed7b8310cbf3c796c3afa81d0407f3282d4bc888356c179ce1ce5bb88cb2cfbbcc5e1c959de00a24e
7
+ data.tar.gz: 21a7493a4c284fb2d24866dab5d39b71eece8507a6f066bb7226bcfd25b28f2e60ec2dcea5c12134413458dda1076f815bda3f8d759cee341114dd8000e99af0
@@ -1,3 +1,3 @@
1
1
  module Niceql
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/niceql.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require "niceql/version"
2
- require 'niceql/string'
3
2
 
4
3
  module Niceql
5
4
 
@@ -45,167 +44,181 @@ module Niceql
45
44
  SQL_COMMENTS_CLEARED = /(\s*?--.+\s{1})|(\s*$\s*\/\*[^\/\*]*\*\/\s{1})/
46
45
  COMMENT_CONTENT = /[\S]+[\s\S]*[\S]+/
47
46
 
48
- def self.config
49
- Niceql.config
50
- end
47
+ class << self
48
+ def config
49
+ Niceql.config
50
+ end
51
51
 
52
- def self.prettify_err(err)
53
- prettify_pg_err( err.to_s )
54
- end
52
+ def prettify_err(err, original_sql_query = nil)
53
+ prettify_pg_err( err.to_s, original_sql_query )
54
+ end
55
55
 
56
56
 
57
- # Postgres error output:
58
- # ERROR: VALUES in FROM must have an alias
59
- # LINE 2: FROM ( VALUES(1), (2) );
60
- # ^
61
- # HINT: For example, FROM (VALUES ...) [AS] foo.
62
-
63
- # May go without HINT or DETAIL:
64
- # ERROR: column "usr" does not exist
65
- # LINE 1: SELECT usr FROM users ORDER BY 1
66
- # ^
67
-
68
- # ActiveRecord::StatementInvalid will add original SQL query to the bottom like this:
69
- # ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "usr" does not exist
70
- # LINE 1: SELECT usr FROM users ORDER BY 1
71
- # ^
72
- #: SELECT usr FROM users ORDER BY 1
73
-
74
- # prettify_pg_err parses ActiveRecord::StatementInvalid string,
75
- # but you may use it without ActiveRecord either way:
76
- # prettify_pg_err( err + "\n" + sql ) OR prettify_pg_err( err, sql )
77
- # don't mess with original sql query, or prettify_pg_err will deliver incorrect results
78
- def self.prettify_pg_err(err, original_sql_query = nil)
79
- return err if err[/LINE \d+/].nil?
80
- err_line_num = err[/LINE \d+/][5..-1].to_i
81
-
82
- #
83
- start_sql_line = err.lines[3][/(HINT|DETAIL)/] ? 4 : 3
84
- err_body = start_sql_line < err.lines.length ? err.lines[start_sql_line..-1] : original_sql_query&.lines
85
-
86
-
87
- # this means original query is missing so it's nothing to prettify
88
- return err unless err_body
89
-
90
- err_quote = ( err.lines[1][/\.\.\..+\.\.\./] && err.lines[1][/\.\.\..+\.\.\./][3..-4] ) ||
91
- ( err.lines[1][/\.\.\..+/] && err.lines[1][/\.\.\..+/][3..-1] )
92
-
93
- # line[2] is err carret line i.e.: ' ^'
94
- # err.lines[1][/LINE \d+:/].length+1..-1 - is a position from error quote begin
95
- err_carret_line = err.lines[2][err.lines[1][/LINE \d+:/].length+1..-1]
96
- # err line will be painted in red completely, so we just remembering it and use
97
- # to replace after paiting the verbs
98
- err_line = err_body[err_line_num-1]
99
-
100
- # when err line is too long postgres quotes it part in double '...'
101
- if err_quote
102
- err_quote_carret_offset = err_carret_line.length - err.lines[1].index( '...' ) + 3
103
- err_carret_line = ' ' * ( err_line.index( err_quote ) + err_quote_carret_offset ) + "^\n"
104
- end
57
+ # Postgres error output:
58
+ # ERROR: VALUES in FROM must have an alias
59
+ # LINE 2: FROM ( VALUES(1), (2) );
60
+ # ^
61
+ # HINT: For example, FROM (VALUES ...) [AS] foo.
105
62
 
106
- err_carret_line = " " + err_carret_line if err_body[0].start_with?(': ')
107
- # if mistake is on last string than err_line.last != \n so we need to prepend \n to carret line
108
- err_carret_line = "\n" + err_carret_line unless err_line[-1] == "\n"
63
+ # May go without HINT or DETAIL:
64
+ # ERROR: column "usr" does not exist
65
+ # LINE 1: SELECT usr FROM users ORDER BY 1
66
+ # ^
109
67
 
110
- #colorizing verbs and strings
111
- err_body = err_body.join.gsub(/#{VERBS}/ ) { |verb| StringColorize.colorize_verb(verb) }
112
- .gsub(STRINGS){ |str| StringColorize.colorize_str(str) }
68
+ # ActiveRecord::StatementInvalid will add original SQL query to the bottom like this:
69
+ # ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "usr" does not exist
70
+ # LINE 1: SELECT usr FROM users ORDER BY 1
71
+ # ^
72
+ #: SELECT usr FROM users ORDER BY 1
113
73
 
114
- #reassemling error message
115
- err_body = err_body.lines
116
- err_body[err_line_num-1]= StringColorize.colorize_err( err_line )
117
- err_body.insert( err_line_num, StringColorize.colorize_err( err_carret_line ) )
74
+ # prettify_pg_err parses ActiveRecord::StatementInvalid string,
75
+ # but you may use it without ActiveRecord either way:
76
+ # prettify_pg_err( err + "\n" + sql ) OR prettify_pg_err( err, sql )
77
+ # don't mess with original sql query, or prettify_pg_err will deliver incorrect results
78
+ def prettify_pg_err(err, original_sql_query = nil)
79
+ return err if err[/LINE \d+/].nil?
80
+ err_line_num = err[/LINE \d+/][5..-1].to_i
81
+ # LINE 1: SELECT usr FROM users ORDER BY 1
82
+ err_address_line = err.lines[1]
118
83
 
119
- err.lines[0..start_sql_line-1].join + err_body.join
120
- end
84
+ start_sql_line = 3 if err.lines.length <= 3
85
+ # error not always contains HINT
86
+ start_sql_line ||= err.lines[3][/(HINT|DETAIL)/] ? 4 : 3
87
+ sql_body = start_sql_line < err.lines.length ? err.lines[start_sql_line..-1] : original_sql_query&.lines
121
88
 
122
- def self.prettify_sql( sql, colorize = true )
123
- indent = 0
124
- parentness = []
125
-
126
- sql = sql.split( SQL_COMMENTS ).each_slice(2).map{ | sql_part, comment |
127
- # remove additional formatting for sql_parts but leave comment intact
128
- [sql_part.gsub(/[\s]+/, ' '),
129
- # comment.match?(/\A\s*$/) - SQL_COMMENTS gets all comment content + all whitespaced chars around
130
- # so this sql_part.length == 0 || comment.match?(/\A\s*$/) checks does the comment starts from new line
131
- comment && ( sql_part.length == 0 || comment.match?(/\A\s*$/) ? "\n#{comment[COMMENT_CONTENT]}\n" : comment[COMMENT_CONTENT] ) ]
132
- }.flatten.join(' ')
133
-
134
- sql.gsub!(/ \n/, "\n")
135
-
136
- sql.gsub!(STRINGS){ |str| StringColorize.colorize_str(str) } if colorize
137
-
138
- first_verb = true
139
- prev_was_comment = false
140
-
141
- sql.gsub!( /(#{VERBS}|#{BRACKETS}|#{SQL_COMMENTS_CLEARED})/) do |verb|
142
- if 'SELECT' == verb
143
- indent += config.indentation_base if !config.open_bracket_is_newliner || parentness.last.nil? || parentness.last[:nested]
144
- parentness.last[:nested] = true if parentness.last
145
- add_new_line = !first_verb
146
- elsif verb == '('
147
- next_closing_bracket = Regexp.last_match.post_match.index(')')
148
- # check if brackets contains SELECT statement
149
- add_new_line = !!Regexp.last_match.post_match[0..next_closing_bracket][/SELECT/] && config.open_bracket_is_newliner
150
- parentness << { nested: add_new_line }
151
- elsif verb == ')'
152
- # this also covers case when right bracket is used without corresponding left one
153
- add_new_line = parentness.last.nil? || parentness.last[:nested]
154
- indent -= ( parentness.last.nil? ? 2 * config.indentation_base : (parentness.last[:nested] ? config.indentation_base : 0) )
155
- indent = 0 if indent < 0
156
- parentness.pop
157
- elsif verb[POSSIBLE_INLINER]
158
- # in postgres ORDER BY can be used in aggregation function this will keep it
159
- # inline with its agg function
160
- add_new_line = parentness.last.nil? || parentness.last[:nested]
161
- else
162
- add_new_line = verb[/(#{INLINE_VERBS})/].nil?
163
- end
89
+ # this means original query is missing so it's nothing to prettify
90
+ return err unless sql_body
164
91
 
165
- # !add_new_line && previous_was_comment means we had newlined comment, and now even
166
- # if verb is inline verb we will need to add new line with indentation BUT all
167
- # inliners match with a space before so we need to strip it
168
- verb.lstrip! if !add_new_line && prev_was_comment
92
+ # err line will be painted in red completely, so we just remembering it and use
93
+ # to replace after painting the verbs
94
+ err_line = sql_body[err_line_num - 1]
169
95
 
170
- add_new_line = prev_was_comment unless add_new_line
171
- add_indent = !first_verb && add_new_line
172
96
 
173
- if verb[SQL_COMMENTS_CLEARED]
174
- verb = verb[COMMENT_CONTENT]
175
- prev_was_comment = true
176
- else
177
- first_verb = false
178
- prev_was_comment = false
97
+ #colorizing verbs and strings
98
+ colorized_sql_body = sql_body.join.gsub(/#{VERBS}/ ) { |verb| StringColorize.colorize_verb(verb) }
99
+ .gsub(STRINGS){ |str| StringColorize.colorize_str(str) }
100
+
101
+ #reassemling error message
102
+ err_body = colorized_sql_body.lines
103
+ # replacing colorized line contained error and adding caret line
104
+ err_body[err_line_num - 1]= StringColorize.colorize_err( err_line )
105
+
106
+ err_caret_line = extract_err_caret_line( err_address_line, err_line, sql_body, err )
107
+ err_body.insert( err_line_num, StringColorize.colorize_err( err_caret_line ) )
108
+
109
+ err.lines[0..start_sql_line-1].join + err_body.join
110
+ end
111
+
112
+ def prettify_sql( sql, colorize = true )
113
+ indent = 0
114
+ parentness = []
115
+
116
+ sql = sql.split( SQL_COMMENTS ).each_slice(2).map{ | sql_part, comment |
117
+ # remove additional formatting for sql_parts but leave comment intact
118
+ [sql_part.gsub(/[\s]+/, ' '),
119
+ # comment.match?(/\A\s*$/) - SQL_COMMENTS gets all comment content + all whitespaced chars around
120
+ # so this sql_part.length == 0 || comment.match?(/\A\s*$/) checks does the comment starts from new line
121
+ comment && ( sql_part.length == 0 || comment.match?(/\A\s*$/) ? "\n#{comment[COMMENT_CONTENT]}\n" : comment[COMMENT_CONTENT] ) ]
122
+ }.flatten.join(' ')
123
+
124
+ sql.gsub!(/ \n/, "\n")
125
+
126
+ sql.gsub!(STRINGS){ |str| StringColorize.colorize_str(str) } if colorize
127
+
128
+ first_verb = true
129
+ prev_was_comment = false
130
+
131
+ sql.gsub!( /(#{VERBS}|#{BRACKETS}|#{SQL_COMMENTS_CLEARED})/) do |verb|
132
+ if 'SELECT' == verb
133
+ indent += config.indentation_base if !config.open_bracket_is_newliner || parentness.last.nil? || parentness.last[:nested]
134
+ parentness.last[:nested] = true if parentness.last
135
+ add_new_line = !first_verb
136
+ elsif verb == '('
137
+ next_closing_bracket = Regexp.last_match.post_match.index(')')
138
+ # check if brackets contains SELECT statement
139
+ add_new_line = !!Regexp.last_match.post_match[0..next_closing_bracket][/SELECT/] && config.open_bracket_is_newliner
140
+ parentness << { nested: add_new_line }
141
+ elsif verb == ')'
142
+ # this also covers case when right bracket is used without corresponding left one
143
+ add_new_line = parentness.last.nil? || parentness.last[:nested]
144
+ indent -= ( parentness.last.nil? ? 2 * config.indentation_base : (parentness.last[:nested] ? config.indentation_base : 0) )
145
+ indent = 0 if indent < 0
146
+ parentness.pop
147
+ elsif verb[POSSIBLE_INLINER]
148
+ # in postgres ORDER BY can be used in aggregation function this will keep it
149
+ # inline with its agg function
150
+ add_new_line = parentness.last.nil? || parentness.last[:nested]
151
+ else
152
+ add_new_line = verb[/(#{INLINE_VERBS})/].nil?
153
+ end
154
+
155
+ # !add_new_line && previous_was_comment means we had newlined comment, and now even
156
+ # if verb is inline verb we will need to add new line with indentation BUT all
157
+ # inliners match with a space before so we need to strip it
158
+ verb.lstrip! if !add_new_line && prev_was_comment
159
+
160
+ add_new_line = prev_was_comment unless add_new_line
161
+ add_indent = !first_verb && add_new_line
162
+
163
+ if verb[SQL_COMMENTS_CLEARED]
164
+ verb = verb[COMMENT_CONTENT]
165
+ prev_was_comment = true
166
+ else
167
+ first_verb = false
168
+ prev_was_comment = false
169
+ end
170
+
171
+ verb = StringColorize.colorize_verb(verb) if !%w[( )].include?(verb) && colorize
172
+
173
+ subs = ( add_indent ? indent_multiline(verb, indent) : verb)
174
+ !first_verb && add_new_line ? "\n" + subs : subs
179
175
  end
180
176
 
181
- verb = StringColorize.colorize_verb(verb) if !['(', ')'].include?(verb) && colorize
177
+ # clear all spaces before newlines, and all whitespaces before strings endings
178
+ sql.tap{ |slf| slf.gsub!( /\s+\n/, "\n" ) }.tap{ |slf| slf.gsub!(/\s+\z/, '') }
179
+ end
182
180
 
183
- subs = ( add_indent ? indent_multiline(verb, indent) : verb)
184
- !first_verb && add_new_line ? "\n" + subs : subs
181
+ def prettify_multiple( sql_multi, colorize = true )
182
+ sql_multi.split( /(?>#{SQL_COMMENTS})|(\;)/ ).inject(['']) { |queries, pattern|
183
+ queries.last << pattern
184
+ queries << '' if pattern == ';'
185
+ queries
186
+ }.map!{ |sql|
187
+ # we were splitting by comments and ;, so if next sql start with comment we've got a misplaced \n\n
188
+ sql.match?(/\A\s+\z/) ? nil : prettify_sql( sql, colorize )
189
+ }.compact.join("\n\n")
185
190
  end
186
191
 
187
- # clear all spaces before newlines, and all whitespaces before string end
188
- sql.tap{ |slf| slf.gsub!( /\s+\n/, "\n" ) }.tap{ |slf| slf.gsub!(/\s+\z/, '') }
189
- end
192
+ private_class_method
193
+ def indent_multiline( verb, indent )
194
+ if verb.match?(/.\s*\n\s*./)
195
+ verb.lines.map!{|ln| ln.prepend(' ' * indent)}.join("\n")
196
+ else
197
+ verb.prepend(' ' * indent)
198
+ end
199
+ end
190
200
 
191
- def self.prettify_multiple( sql_multi, colorize = true )
192
- sql_multi.split( /(?>#{SQL_COMMENTS})|(\;)/ ).inject(['']) { |queries, pattern|
193
- queries.last << pattern
194
- queries << '' if pattern == ';'
195
- queries
196
- }.map!{ |sql|
197
- # we were splitting by comments and ;, so if next sql start with comment we've got a misplaced \n\n
198
- sql.match?(/\A\s+\z/) ? nil : prettify_sql( sql, colorize )
199
- }.compact.join("\n\n")
200
- end
201
+ def extract_err_caret_line( err_address_line, err_line, sql_body, err )
202
+ # LINE could be quoted ( both sides and sometimes only from one ):
203
+ # "LINE 1: ...t_id\" = $13 AND \"products\".\"carrier_id\" = $14 AND \"product_t...\n",
204
+ err_quote = (err_address_line.match(/\.\.\.(.+)\.\.\./) || err_address_line.match(/\.\.\.(.+)/) ).try(:[], 1)
205
+
206
+ # line[2] is original err caret line i.e.: ' ^'
207
+ # err_address_line[/LINE \d+:/].length+1..-1 - is a position from error quote begin
208
+ err_caret_line = err.lines[2][err_address_line[/LINE \d+:/].length+1..-1]
209
+
210
+ # when err line is too long postgres quotes it in double '...'
211
+ # so we need to reposition caret against original line
212
+ if err_quote
213
+ err_quote_caret_offset = err_caret_line.length - err_address_line.index( '...' ).to_i + 3
214
+ err_caret_line = ' ' * ( err_line.index( err_quote ) + err_quote_caret_offset ) + "^\n"
215
+ end
201
216
 
202
- private
203
- def self.indent_multiline( verb, indent )
204
- #
205
- if verb.match?(/.\s*\n\s*./)
206
- verb.lines.map!{|ln| "#{' ' * indent}" + ln}.join("\n")
207
- else
208
- "#{' ' * indent}" + verb.to_s
217
+ # older versions of ActiveRecord were adding ': ' before an original query :(
218
+ err_caret_line.prepend(' ') if sql_body[0].start_with?(': ')
219
+ # if mistake is on last string than err_line.last != \n then we need to prepend \n to caret line
220
+ err_caret_line.prepend("\n") unless err_line[-1] == "\n"
221
+ err_caret_line
209
222
  end
210
223
  end
211
224
  end
@@ -228,7 +241,9 @@ module Niceql
228
241
 
229
242
  module ErrorExt
230
243
  def to_s
231
- Niceql.config.prettify_pg_errors ? Prettifier.prettify_err(super) : super
244
+ # older rails version do not provide sql as a standalone query, instead they
245
+ # deliver joined message
246
+ Niceql.config.prettify_pg_errors ? Prettifier.prettify_err(super, try(:sql) ) : super
232
247
  end
233
248
  end
234
249
 
@@ -280,5 +295,3 @@ module Niceql
280
295
  end
281
296
 
282
297
  end
283
-
284
-
data/niceql.gemspec CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ["lib"]
32
32
 
33
- spec.required_ruby_version = '>= 2.3'
33
+ spec.required_ruby_version = '>= 2.4'
34
34
  spec.add_development_dependency "activerecord", ">= 6.1"
35
35
 
36
36
  spec.add_development_dependency "bundler", ">= 1"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: niceql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - alekseyl
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-22 00:00:00.000000000 Z
11
+ date: 2021-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -147,7 +147,6 @@ files:
147
147
  - lib/generators/niceql/install_generator.rb
148
148
  - lib/generators/templates/niceql_initializer.rb
149
149
  - lib/niceql.rb
150
- - lib/niceql/string.rb
151
150
  - lib/niceql/version.rb
152
151
  - niceql.gemspec
153
152
  - to_niceql.png
@@ -164,7 +163,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
164
163
  requirements:
165
164
  - - ">="
166
165
  - !ruby/object:Gem::Version
167
- version: '2.3'
166
+ version: '2.4'
168
167
  required_rubygems_version: !ruby/object:Gem::Requirement
169
168
  requirements:
170
169
  - - ">="
data/lib/niceql/string.rb DELETED
@@ -1,5 +0,0 @@
1
- class String
2
- def match?(pattern)
3
- self =~ pattern
4
- end
5
- end unless String.method_defined?(:match?)