Class: QueryBuilder::Processor

Inherits:
Object
  • Object
show all
Defined in:
lib/query_builder/processor.rb

Constant Summary collapse

OPERATOR_TO_METHOD =

class << self

{
  :"!" => :not,
  :"@-" => :change_sign, :"@~" => :invert_bits,
  :"^" => :bitwise_xor,
  :* => :times, :/ => :division, :DIV => :integer_division, :% => :modulo, :mod => :modulo,
  :- => :minus, :+ => :addition,
  :<< => :left_shift, :>> => :right_shift,
  :& => :bitwise_and,
  :| => :bitwise_or,
  :eq => :equal, :ge => :greater_or_equal,
  :gt => :greater, :le => :less_or_equal, :lt => :less, :ne => :not_equal,
  :"=" => :equal, :"<=>" => :null_safe_equal, :>= => :greater_or_equal,
  :> => :greater, :<= => :less_or_equal, :< => :less, :"<>" => :not_equal, :"!=" => :not_equal,
  :"&&" => :and,
  :"||" => :or,
  :":=" => :assign
}
METHOD_TO_OPERATOR =
{
  :greater => :>, :greater_or_equal => :>=,
  :equal   => :'=',
  :less_or_equal => :<=, :less => :<,
  :not_equal => :'<>'
}

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, opts = {}) ⇒ Processor

Returns a new instance of Processor.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/query_builder/processor.rb', line 140

def initialize(source, opts = {})
  @default = opts.delete(:default) || {}
  @opts = opts
  @rubyless_helper  = @opts[:rubyless_helper]

  if source.kind_of?(Processor)
    # experimental: class change
    @context  = source.context
    @query    = source.query
    @sxp      = source.sxp
    @ancestor = source # used during class change to return back to previous 'this'
  elsif source.kind_of?(String)
    @sxp = Parser.parse(source)

    @context = opts.merge(:first => true, :last => true)
    @query = Query.new(self.class)
    @query.main_class = @opts[:main_class] || resolve_main_class(self.class.main_class)
    before_process

    process_all

    after_process

    if limit = @opts[:limit]
      @query.limit = " LIMIT #{limit}"
    end
  else
    raise QueryBuilder::Error.new("Cannot use #{source.class} as source: expected a String or QueryBuilder::Processor")
  end
rescue QueryBuilder::Error => err
  raise err
rescue Exception => err
  raise QueryBuilder::Error.new("Exception raised while processing '#{source}' (#{err.message})")
end

Class Attribute Details

.after_process_callbacksObject

Returns the value of attribute after_process_callbacks.



11
12
13
# File 'lib/query_builder/processor.rb', line 11

def after_process_callbacks
  @after_process_callbacks
end

.before_process_callbacksObject

Returns the value of attribute before_process_callbacks.



11
12
13
# File 'lib/query_builder/processor.rb', line 11

def before_process_callbacks
  @before_process_callbacks
end

.custom_queriesObject

class variable



9
10
11
# File 'lib/query_builder/processor.rb', line 9

def custom_queries
  @custom_queries
end

.custom_query_filesObject

class variable



9
10
11
# File 'lib/query_builder/processor.rb', line 9

def custom_query_files
  @custom_query_files
end

.defaultsObject

Returns the value of attribute defaults.



10
11
12
# File 'lib/query_builder/processor.rb', line 10

def defaults
  @defaults
end

.main_classObject

class variable



9
10
11
# File 'lib/query_builder/processor.rb', line 9

def main_class
  @main_class
end

.main_tableObject

class variable



9
10
11
# File 'lib/query_builder/processor.rb', line 9

def main_table
  @main_table
end

Instance Attribute Details

#ancestorObject (readonly)

Returns the value of attribute ancestor.



5
6
7
# File 'lib/query_builder/processor.rb', line 5

def ancestor
  @ancestor
end

#contextObject (readonly)

Returns the value of attribute context.



5
6
7
# File 'lib/query_builder/processor.rb', line 5

def context
  @context
end

#queryObject (readonly)

Returns the value of attribute query.



5
6
7
# File 'lib/query_builder/processor.rb', line 5

def query
  @query
end

#sxpObject (readonly)

Returns the value of attribute sxp.



5
6
7
# File 'lib/query_builder/processor.rb', line 5

def sxp
  @sxp
end

Class Method Details

.after_process(callback) ⇒ Object



30
31
32
# File 'lib/query_builder/processor.rb', line 30

def after_process(callback)
  (self.after_process_callbacks ||= []) << callback
end

.before_process(callback) ⇒ Object



26
27
28
# File 'lib/query_builder/processor.rb', line 26

def before_process(callback)
  (self.before_process_callbacks ||= []) << callback
end

.insert_bind(str) ⇒ Object



34
35
36
37
38
39
40
# File 'lib/query_builder/processor.rb', line 34

def insert_bind(str)
  if str.kind_of?(::RubyLess::TypedString)
    ::RubyLess::TypedString.new("[[#{str}]]", str.opts)
  else
    "[[#{str}]]"
  end
end

.load_custom_queries(directories) ⇒ Object

Load prepared SQL definitions from a set of directories. If the file does not contain “group” or “groups” keys, the filename is used as group.

Parameters

query<String>

Path to list of custom queries yaml files.

Examples

DummyQuery.load_custom_queries("/path/to/some/*/directory")

The format of a custom query definition is:

groups:
  - test.host
DummyQuery:      # QueryBuilder class
  abc:           # query's relation name
    select:      # selected fields
      - 'a'
      - '34 AS number'
      - 'c'
    tables:      # tables used
      - 'test'
    join_tables: # joins
      test:
        - LEFT JOIN other ON other.test_id = test.id
    where:    # filters
      - '1'
      - '2'
      - '3'
    order:  'a DESC' # order clause

Once loaded, this ‘custom query’ can be used in a query like:

"images from abc where a > 54"


74
75
76
77
78
# File 'lib/query_builder/processor.rb', line 74

def load_custom_queries(directories)
  # lazy loading (evaluation happens on first query)
  self.custom_query_files ||= []
  self.custom_query_files << directories
end

.load_custom_queries!Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/query_builder/processor.rb', line 80

def load_custom_queries!
  return unless list = self.custom_query_files
  klass = nil
  self.custom_queries ||= {'all' => {}}
  Dir.glob(list.flatten).each do |dir|
    if File.directory?(dir)
      Dir.foreach(dir) do |file|
        next unless file =~ /(.+).yml$/
        custom_query_groups = $1
        definitions = YAML::load(File.read(File.join(dir,file)))
        custom_query_groups = [definitions.delete('groups') || definitions.delete('group') || custom_query_groups].flatten
        definitions.each do |klass, query_list|
          begin
            klass = QueryBuilder.resolve_const(klass)
            klass = klass.query_compiler if klass.respond_to?(:query_compiler)
            raise ArgumentError.new("Invalid Processor class '#{klass}' in '#{file}' custom query. Should be a descendant of QueryBuilder::Processor or respond to query_compiler.") unless klass.ancestors.include?(Processor)
          rescue NameError
            raise ArgumentError.new("Unknown Processor class '#{klass}' in '#{file}' custom query.")
          end
          custom_queries = klass.custom_queries
          custom_query_groups.each do |custom_query_group|
            custom_queries[custom_query_group] ||= {}
            klass_queries = custom_queries[custom_query_group]
            query_list.each do |query_name, query|
              klass_queries[query_name] = query
            end
          end
        end
      end
    end
  end
  self.custom_query_files = nil
end

.set_default(key, value) ⇒ Object



21
22
23
24
# File 'lib/query_builder/processor.rb', line 21

def set_default(key, value)
  self.defaults ||= {}
  self.defaults[key] = value
end

.set_main_class(klass) ⇒ Object



17
18
19
# File 'lib/query_builder/processor.rb', line 17

def set_main_class(klass)
  self.main_class = klass
end

.set_main_table(table_name) ⇒ Object



13
14
15
# File 'lib/query_builder/processor.rb', line 13

def set_main_table(table_name)
  self.main_table = table_name
end