Class: SqlBeautifier::Condition

Inherits:
Base
  • Object
show all
Defined in:
lib/sql_beautifier/condition.rb

Constant Summary collapse

NOT_PREFIX_PATTERN =
%r{\Anot([[:space:]]+)}i

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.build(conjunction, condition_text) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/sql_beautifier/condition.rb', line 68

def self.build(conjunction, condition_text)
  unwrapped = unwrap_single_condition(condition_text)
  inner_conditions = parse_condition_group(unwrapped)

  if inner_conditions
    children = inner_conditions.map do |inner_conjunction, inner_text|
      build(inner_conjunction, inner_text)
    end

    new(conjunction: conjunction, children: children)
  else
    new(conjunction: conjunction, expression: unwrapped)
  end
end

.flatten_group_into!(flattened, group_condition, outer_conjunction) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/sql_beautifier/condition.rb', line 158

def self.flatten_group_into!(flattened, group_condition, outer_conjunction)
  group_condition.children.each_with_index do |child, inner_index|
    new_conjunction = begin
      if flattened.empty?
        nil
      elsif inner_index.zero?
        group_condition.conjunction || outer_conjunction
      else
        outer_conjunction
      end
    end

    flattened << new(conjunction: new_conjunction, expression: child.expression, children: child.children)
  end
end

.flatten_same_conjunction_groups(conditions) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/sql_beautifier/condition.rb', line 131

def self.flatten_same_conjunction_groups(conditions)
  return conditions if conditions.length <= 1

  outer_conjunction = conditions[1]&.conjunction
  return conditions unless outer_conjunction
  return conditions unless conditions.drop(1).all? { |condition| condition.conjunction == outer_conjunction }

  flattened = []

  conditions.each do |condition|
    if condition.group? && flattenable_into_conjunction?(condition, outer_conjunction)
      flatten_group_into!(flattened, condition, outer_conjunction)
    else
      flattened << condition
    end
  end

  flattened
end

.flattenable_into_conjunction?(condition, outer_conjunction) ⇒ Boolean

Returns:

  • (Boolean)


151
152
153
154
155
156
# File 'lib/sql_beautifier/condition.rb', line 151

def self.flattenable_into_conjunction?(condition, outer_conjunction)
  return false unless condition.group?

  inner_conjunction = condition.children[1]&.conjunction
  inner_conjunction == outer_conjunction && condition.children.drop(1).all? { |child| child.conjunction == outer_conjunction }
end

.format(text, indent_width: 0) ⇒ Object



11
12
13
14
15
16
# File 'lib/sql_beautifier/condition.rb', line 11

def self.format(text, indent_width: 0)
  conditions = parse_all(text)
  return conditions.first.expression if conditions.length <= 1 && conditions.first&.leaf?

  render_all(conditions, indent_width: indent_width)
end

.parse_all(text) ⇒ Object



18
19
20
21
22
23
24
25
# File 'lib/sql_beautifier/condition.rb', line 18

def self.parse_all(text)
  raw_pairs = Tokenizer.split_top_level_conditions(text)
  conditions = raw_pairs.map do |conjunction, condition_text|
    build(conjunction, condition_text)
  end

  flatten_same_conjunction_groups(conditions)
end

.parse_condition_group(condition_text) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/sql_beautifier/condition.rb', line 118

def self.parse_condition_group(condition_text)
  return unless condition_text

  trimmed = condition_text.strip
  return unless Tokenizer.outer_parentheses_wrap_all?(trimmed)

  inner_content = Util.strip_outer_parentheses(trimmed)
  inner_conditions = Tokenizer.split_top_level_conditions(inner_content)
  return unless inner_conditions.length > 1

  inner_conditions
end

.render_all(conditions, indent_width:) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/sql_beautifier/condition.rb', line 27

def self.render_all(conditions, indent_width:)
  indentation = Util.whitespace(indent_width)
  lines = []

  conditions.each_with_index do |condition, index|
    rendered = condition.render(indent_width: indent_width)

    line = begin
      if index.zero?
        "#{indentation}#{rendered}"
      else
        "#{indentation}#{condition.conjunction} #{rendered}"
      end
    end

    lines << line
  end

  lines.join("\n")
end

.unwrap_not_prefix_parens(text) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/sql_beautifier/condition.rb', line 97

def self.unwrap_not_prefix_parens(text)
  prefix_match = text.match(NOT_PREFIX_PATTERN)
  return text unless prefix_match

  remainder = text[prefix_match[0].length..]

  unwrapped_remainder = remainder

  while Tokenizer.outer_parentheses_wrap_all?(unwrapped_remainder)
    inner_content = Util.strip_outer_parentheses(unwrapped_remainder)
    inner_conditions = Tokenizer.split_top_level_conditions(inner_content)
    break if inner_conditions.length > 1

    unwrapped_remainder = inner_content
  end

  return text if unwrapped_remainder == remainder

  "not #{unwrapped_remainder}"
end

.unwrap_single_condition(condition_text) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/sql_beautifier/condition.rb', line 83

def self.unwrap_single_condition(condition_text)
  output = condition_text.strip

  while Tokenizer.outer_parentheses_wrap_all?(output)
    inner_content = Util.strip_outer_parentheses(output)
    inner_conditions = Tokenizer.split_top_level_conditions(inner_content)
    break if inner_conditions.length > 1

    output = inner_content
  end

  unwrap_not_prefix_parens(output)
end

Instance Method Details

#group? ⇒ Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/sql_beautifier/condition.rb', line 52

def group?
  !leaf?
end

#leaf? ⇒ Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/sql_beautifier/condition.rb', line 48

def leaf?
  @children.nil?
end

#render(indent_width:) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/sql_beautifier/condition.rb', line 56

def render(indent_width:)
  return InList.format_in_text(@expression, base_indent: indent_width) if leaf?

  inline_version = render_inline
  return inline_version if inline_version.length <= SqlBeautifier.config_for(:inline_group_threshold)

  inner_output = self.class.render_all(@children, indent_width: indent_width + 4)
  closing_indentation = Util.whitespace(indent_width)

  "(\n#{inner_output}\n#{closing_indentation})"
end