lib/niceql.rb in niceql-0.1.9 vs lib/niceql.rb in niceql-0.1.10

- old
+ new

@@ -17,39 +17,39 @@ end end module ArExtentions def exec_niceql - begin - connection.execute( to_niceql ) - rescue StandardError => e - raise e.class.new( Prettifier.prettify_err(e ) ) - end + connection.execute( to_niceql ) end def to_niceql Prettifier.prettify_sql(to_sql, false) end def niceql( colorize = true ) puts Prettifier.prettify_sql( to_sql, colorize ) end + end module Prettifier - INLINE_VERBS = %w(ASC IN AS WHEN THEN ELSE END AND UNION ALL WITH ON DISTINCT INTERSECT EXCEPT EXISTS NOT COUNT).join('| ') - NEW_LINE_VERBS = 'SELECT|FROM|WHERE|CASE|ORDER BY|LIMIT|GROUP BY|WITH|LEFT JOIN|RIGHT JOIN|JOIN|HAVING|OFFSET' + INLINE_VERBS = %w(WITH ASC IN COALESCE AS WHEN THEN ELSE END AND UNION ALL WITH ON DISTINCT INTERSECT EXCEPT EXISTS NOT COUNT ROUND CAST).join('| ') + NEW_LINE_VERBS = 'SELECT|FROM|WHERE|CASE|ORDER BY|LIMIT|GROUP BY|LEFT JOIN|RIGHT JOIN|JOIN|HAVING|OFFSET|UPDATE' + POSSIBLE_INLINER = /(ORDER BY|CASE)/ VERBS = "#{INLINE_VERBS}|#{NEW_LINE_VERBS}" STRINGS = /("[^"]+")|('[^']+')/ BRACKETS = '[\(\)]' + + def self.config + Niceql.config + end + + def self.prettify_err(err) - if ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'postgresql' - prettify_pg_err( err.to_s ) - else - err - end + prettify_pg_err( err.to_s ) end def self.prettify_pg_err(err) err_line_num = err[/LINE \d+/][5..-1].to_i @@ -57,25 +57,25 @@ start_sql_line = err.lines[3][/(HINT|DETAIL)/] ? 4 : 3 err_body = err.lines[start_sql_line..-1] err_quote = ( err.lines[1][/\.\.\..+\.\.\./] && err.lines[1][/\.\.\..+\.\.\./][3..-4] ) || ( err.lines[1][/\.\.\..+/] && err.lines[1][/\.\.\..+/][3..-1] ) - # line 2 is err carret line + # err.lines[1][/LINE \d+:/].length+1..-1 - is a position from error quote begin err_carret_line = err.lines[2][err.lines[1][/LINE \d+:/].length+1..-1] # err line painted red completly, so we just remembering it and use # to replace after paiting the verbs err_line = err_body[err_line_num-1] # when err line is too long postgres quotes it part in doble ... if err_quote err_quote_carret_offset = err_carret_line.length - err.lines[1].index( '...' ) + 3 - err_carret_line = ' ' * ( err_line.index( err_quote ) + err_quote_carret_offset ) + err_carret_line = ' ' * ( err_line.index( err_quote ) + err_quote_carret_offset ) + "^\n" end # if mistake is on last string than err_line.last != \n so we need to prepend \n to carret line - err_line = ( err_line.last == "\n" ? err_line + "\n" : "\n" + err_line ) + err_carret_line = "\n" + err_carret_line unless err_line.last == "\n" #colorizing verbs and strings err_body = err_body.join.gsub(/#{VERBS}/ ) { |verb| StringColorize.colorize_verb(verb) } err_body = err_body.gsub(STRINGS){ |str| StringColorize.colorize_str(str) } @@ -89,72 +89,94 @@ def self.prettify_sql( sql, colorize = true ) indent = 0 parentness = [] + #it's better to remove all new lines because it will break formatting + sql = sql.gsub("\n", ' ') + # remove any additional formatting + sql = sql.gsub(/[ ]+/, ' ') + sql = sql.gsub(STRINGS){ |str| StringColorize.colorize_str(str) } if colorize first_verb = true - sql.gsub( /(#{VERBS}|#{BRACKETS})/) do |verb| + sql.gsub( /(#{VERBS}|#{BRACKETS})/).with_index do |verb, index| add_new_line = false if 'SELECT' == verb - indent += 1 + indent += config.indentation_base if parentness.last.nil? || parentness.last[:nested] parentness.last[:nested] = true if parentness.last add_new_line = !first_verb - first_verb = false elsif verb == '(' - parentness << { nested: false } - indent += 1 + next_closing_bracket = Regexp.last_match.post_match.index(')') + add_new_line = !!Regexp.last_match.post_match[0..next_closing_bracket][/SELECT/] && config.open_bracket_is_newliner + parentness << { nested: add_new_line } elsif verb == ')' # this also covers case when right bracket is used without corresponding left one add_new_line = parentness.last.nil? || parentness.last[:nested] - indent -= add_new_line ? 2 : 1 + indent -= ( parentness.last.nil? ? 2 * config.indentation_base : (parentness.last[:nested] ? config.indentation_base : 0) ) indent = 0 if indent < 0 parentness.pop - elsif verb == 'ORDER BY' + elsif verb[POSSIBLE_INLINER] # in postgres ORDER BY can be used in aggregation function this will keep it # inline with its agg function add_new_line = parentness.last.nil? || parentness.last[:nested] else add_new_line = verb[/(#{INLINE_VERBS})/].nil? end + first_verb = false verb = StringColorize.colorize_verb(verb) if !['(', ')'].include?(verb) && colorize add_new_line ? "\n#{' ' * indent}" + verb : verb end end end module PostgresAdapterNiceQL def exec_query(sql, name = "SQL", binds = [], prepare: false) - begin - # replacing sql with prettified sql, thats all - super( Prettifier.prettify_sql(sql, false), name, binds, prepare: prepare ) - rescue StandardError => e - raise e.class.new( Prettifier.prettify_err(e ) ) + # replacing sql with prettified sql, thats all + super( Prettifier.prettify_sql(sql, false), name, binds, prepare: prepare ) + end + end + + module ErrorExt + def to_s + if ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'postgresql' + Prettifier.prettify_err( super ) + else + super end end end class NiceQLConfig attr_accessor :pg_adapter_with_nicesql + + attr_accessor :indentation_base + + attr_accessor :open_bracket_is_new_liner + def initialize self.pg_adapter_with_nicesql = false + self.indentation_base = 2 + self.open_bracket_is_new_liner = false end end def self.configure - @config ||= NiceQLConfig.new + yield( config ) - yield( @config ) - - if @config.pg_adapter_with_nicesql + if config.pg_adapter_with_nicesql ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.include(PostgresAdapterNiceQL) end end + def self.config + @config ||= NiceQLConfig.new + end + if defined? ::ActiveRecord::Base + ActiveRecord::StatementInvalid.include( Niceql::ErrorExt ) ::ActiveRecord::Base.extend ArExtentions [::ActiveRecord::Relation, ::ActiveRecord::Associations::CollectionProxy].each { |klass| klass.send(:include, ArExtentions) } end end