Class: CSDL::Builder

Inherits:
Object
  • Object
show all
Includes:
AST::Sexp
Defined in:
lib/csdl/builder.rb

Overview

Builder is a class used to produce AST::Node objects built to be processed by any one of Processor, InteractionFilterProcessor, or QueryFilterProcessor.

Examples:

# Generate your CSDL nodes using the Builder
root_node = CSDL::Builder.new.logical_group(:or) do
  #...
  condition("fb.content", :contains, "match this string")
  #...
end

# Process the root node and its children calling the instance method #process
CSDL::Processor.new.process(root_node)

See Also:

Instance Method Summary collapse

Instance Method Details

#_and(&block) ⇒ AST::Node

Logically AND two or more child nodes together. Does not implicitly create a logical group with parentheses. If you want to logically group the ANDs, see #logical_group.

Examples:

nodes = CSDL::Builder.new._and do
  [
    condition("fb.content", :contains, "this is a string"),
    condition("fb.parent.content", :contains, "this is a string"),
  ]
end
CSDL::Processor.new.process(nodes) # => 'fb.content contains "this is a string" AND fb.parent.content contains "this is a string"'

Yield Returns:

  • (AST::Node, Array<AST::Node>)

    An AST node or array of AST nodes to be wrapped by an :and node.

See Also:


41
42
43
44
45
46
47
48
49
50
# File 'lib/csdl/builder.rb', line 41

def _and(&block)
  children = __one_or_more_child_nodes(&block)
  if children.empty?
    nil
  elsif children.size == 1
    children.first
  else
    s(:and, *children)
  end
end

#_not(target, operator, argument = nil) ⇒ AST::Node

Negate a condition. Analogous to #condition with NOT prepended to the condition.

Examples:

node = CSDL::Builder.new._not("fb.content", :contains, "do not match this string")
CSDL::Processor.new.process(node) # => 'NOT fb.content contains "do not match this string"'

Multiple negations ANDed together

nodes = CSDL::Builder.new._and do
  [
    _not("fb.content", :contains, "this is a string"),
    _not("fb.parent.content", :contains, "this is a string"),
  ]
end
CSDL::Processor.new.process(nodes) # => 'NOT fb.content contains "this is a string" AND NOT fb.parent.content contains "this is a string"'

See Also:


75
76
77
78
# File 'lib/csdl/builder.rb', line 75

def _not(target, operator, argument = nil)
  node = condition(target, operator, argument)
  node.updated(:not)
end

#_or(&block) ⇒ AST::Node

Logically OR two or more child nodes together. Does not implicitly create a logical group with parentheses. If you want to logically group the ORs, see #logical_group.

Examples:

nodes = CSDL::Builder.new._or do
  [
    condition("fb.content", :contains, "this is a string"),
    condition("fb.parent.content", :contains, "this is a string")
  ]
end
CSDL::Processor.new.process(nodes) # => 'fb.content contains "this is a string" OR fb.parent.content contains "this is a string"'

Yield Returns:

  • (AST::Node, Array<AST::Node>)

    An AST node or array of AST nodes to be wrapped by an :or node.

See Also:


98
99
100
101
102
103
104
105
106
107
# File 'lib/csdl/builder.rb', line 98

def _or(&block)
  children = __one_or_more_child_nodes(&block)
  if children.empty?
    nil
  elsif children.size == 1
    children.first
  else
    s(:or, *children)
  end
end

#_return(&block) ⇒ AST::Node

Note:

The base Processor will not process return statement nodes, use InteractionFilterProcessor instead.

Wrap child nodes in a return statement scope.

Examples:

nodes = CSDL::Builder.new._return do
  condition("fb.content", :contains, "this is a string")
end
CSDL::InteractionFilterProcessor.new.process(nodes) # => 'return {fb.content contains "this is a string"}'

Yield Returns:

  • (AST::Node, Array<AST::Node>)

    An AST node or array of AST nodes to be wrapped by a :statement_scope node.

See Also:


126
127
128
# File 'lib/csdl/builder.rb', line 126

def _return(&block)
  s(:return, statement_scope(&block))
end

#condition(target, operator, argument = nil) ⇒ AST::Node

Create a “target + operator[ + argument]” CSDL condition. This method is the workhorse of any CSDL Filter. See #_not if you wish to negate a single condition.

Examples:

node = CSDL::Builder.new.condition("fb.content", :contains, "match this string")
CSDL::Processor.new.process(node) # => 'fb.content contains "match this string"'

Multiple conditions ANDed together

nodes = CSDL::Builder.new._and do
  [
    condition("fb.content", :contains, "this is a string"),
    condition("fb.parent.content", :contains, "this is a string"),
  ]
end
CSDL::Processor.new.process(nodes) # => 'fb.content contains "this is a string" AND fb.parent.content contains "this is a string"'

See Also:


154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/csdl/builder.rb', line 154

def condition(target, operator, argument = nil)
  target_node   = s(:target, target)
  operator_node = s(:operator, operator)
  argument_node = nil

  unless argument.nil?
    argument_node_type  = argument.class.name.to_s.downcase.to_sym
    child_argument_node = s(argument_node_type, argument)
    argument_node       = s(:argument, child_argument_node)
  end

  s(:condition, *[target_node, operator_node, argument_node].compact)
end

#logical_group(logical_operator = nil, &block) ⇒ AST::Node

Wrap any child nodes in a logical grouping with parentheses. Additionally specify a logical operator to wrap all block child nodes. See #_or and #_and.

Examples:

nodes = CSDL::Builder.new.logical_group do
  condition("fb.content", :contains, "this is a string")
end
CSDL::Processor.new.process(nodes) # => '(fb.content contains "this is a string")'

Without logical operator argument (default)

nodes = CSDL::Builder.new.logical_group do
  _or do
    [
      condition("fb.content", :contains, "this is a string"),
      condition("fb.parent.content", :contains, "this is a string")
    ]
  end
end
CSDL::Processor.new.process(nodes) # => '(fb.content contains "this is a string" OR fb.parent.content contains "this is a string")'

With logical operator argument, notice removal of _or block from previous example

nodes = CSDL::Builder.new.logical_group(:or) do
  [
    condition("fb.content", :contains, "this is a string"),
    condition("fb.parent.content", :contains, "this is a string")
  ]
end
CSDL::Processor.new.process(nodes) # => '(fb.content contains "this is a string" OR fb.parent.content contains "this is a string")'

Complex example

nodes = CSDL::Builder.new._and do
  [
    logical_group(:or) {
      [
        condition("fb.content", :contains, "this is a string"),
        condition("fb.parent.content", :contains, "this is a string")
      ]
    },
    logical_group(:or) {
      [
        condition("fb.author.age", :==, "25-34"),
        condition("fb.parent.author.age", :==, "25-34")
      ]
    },
    logical_group(:or) {
      [
        condition("fb.author.gender", :==, "male"),
        condition("fb.parent.author.gender", :==, "male")
      ]
    },
    condition("fb.author.region", :==, "texas")
  ]
end
CSDL::Processor.new.process(nodes) # => '(fb.content contains "this is a string" OR fb.parent.content contains "this is a string") AND (fb.author.age == "25-34" OR fb.parent.author.age == "25-34") AND (fb.author.gender == "male" OR fb.parent.author.gender == "male") AND fb.author.region == "texas"'

Yield Returns:

  • (AST::Node, Array<AST::Node>)

    An AST node or array of AST nodes to be wrapped by a :logical_group node (and possibly also a node for the logical operator).

See Also:


231
232
233
234
235
236
237
238
239
240
241
# File 'lib/csdl/builder.rb', line 231

def logical_group(logical_operator = nil, &block)
  children = __one_or_more_child_nodes(&block)

  if children.empty?
    nil
  elsif logical_operator.nil? || children.size == 1
    s(:logical_group, *children)
  else
    s(:logical_group, s(logical_operator, *children))
  end
end

#raw(raw_csdl) ⇒ AST::Node

Note:

this method will not implicitly wrap the raw CSDL in any grouping or scope.

Create a node to store raw CSDL.

Examples:

node = CSDL::Builder.new.raw(%q{fb.content contains_any "foo" OR fb.parent.content contains_any "foo"})
CSDL::Processor.new.process(node) # => %q{fb.content contains_any "foo" OR fb.parent.content contains_any "foo"}

Using with other generated nodes (e.g. condition)

nodes = CSDL::Builder.new._or do
  [
    condition("fb.type", :exists),
    logical_group { raw(%q{fb.content contains_any "foo" OR fb.parent.content contains_any "foo"}) }
  ]
end
CSDL::Processor.new.process(nodes) # => 'fb.type exists OR (fb.content contains_any "foo" OR fb.parent.content contains_any "foo")'

263
264
265
# File 'lib/csdl/builder.rb', line 263

def raw(raw_csdl)
  s(:raw, raw_csdl.to_s)
end

#root(&block) ⇒ AST::Node

Wrap child nodes in a root node. Useful for building CSDL with tagging and a return statement.

Examples:

nodes = CSDL::Builder.new.root do
  [
    tag_tree(["movies"], "Video") { condition("links.url", :any, "youtube.com,vimeo.com") },
    tag_tree(["movies"], "Social Networks") { condition("links.url", :any, "twitter.com,facebook.com") },

    return {
      _or {
        [
          condition("fb.topics.category", :in, "Movie,Film,TV"),
          condition("fb.parent.topics.category", :in, "Movie,Film,TV")
        ]
      }
    }
  ]
end
CSDL::InteractionFilterProcessor.new.process(nodes) # => 'tag.movies "Video" {links.url any "youtube.com,vimeo.com"} tag.movies "Social Networks" {links.url any "twitter.com,facebook.com"} return {fb.topics.category in "Movie,Film,TV" OR fb.parent.topics.cateogry in "Movie,Film,TV"}'

Yield Returns:

  • (AST::Node, Array<AST::Node>)

    An AST node or array of AST nodes to be wrapped by a :statement_scope node.

See Also:


295
296
297
298
299
300
301
302
# File 'lib/csdl/builder.rb', line 295

def root(&block)
  children = __one_or_more_child_nodes(&block)
  if children.empty?
    nil
  else
    s(:root, *children)
  end
end

#statement_scope(&block) ⇒ AST::Node

Note:

The base Processor will not process statement_scope nodes, use InteractionFilterProcessor instead.

Wrap child nodes in braces. @note Generally not useful on its own, see #_return, #tag, or #tag_tree usage.

Examples:

nodes = CSDL::Builder.new.statement_scope do
  condition("fb.content", :contains, "this is a string")
end
CSDL::InteractionFilterProcessor.new.process(nodes) # => '{fb.content contains "this is a string"}'

Yield Returns:

  • (AST::Node, Array<AST::Node>)

    An AST node or array of AST nodes to be wrapped by a :statement_scope node.

See Also:


323
324
325
326
327
328
329
330
# File 'lib/csdl/builder.rb', line 323

def statement_scope(&block)
  children = __one_or_more_child_nodes(&block)
  if children.empty?
    nil
  else
    s(:statement_scope, *children)
  end
end

#tag(tag_class, &block) ⇒ AST::Node

Note:

The base Processor will not process tag nodes, use InteractionFilterProcessor instead.

Wrap child nodes in a VEDO tag classification.

Examples:

nodes = CSDL::Builder.new.tag("MyTag") do
  condition("fb.content", :contains, "this is a string")
end
CSDL::InteractionFilterProcessor.new.process(nodes) # => 'tag "MyTag" {fb.content contains "this is a string"}'

Yield Returns:

  • (AST::Node, Array<AST::Node>)

    An AST node or array of AST nodes to be wrapped by a :statement_scope node, to be a child of the returned :tag node.

See Also:


351
352
353
354
355
356
# File 'lib/csdl/builder.rb', line 351

def tag(tag_class, &block)
  s(:tag,
    s(:tag_class,
      s(:string, tag_class)),
    statement_scope(&block))
end

#tag_tree(tag_namespaces, tag_class, &block) ⇒ AST::Node

Note:

The base Processor will not process tag_tree nodes, use InteractionFilterProcessor instead.

Wrap child nodes in a VEDO tag classification tree.

Examples:

nodes = CSDL::Builder.new.tag_tree(%w(foo bar), "MyTag") do
  condition("fb.content", :contains, "this is a string")
end
CSDL::InteractionFilterProcessor.new.process(nodes) # => 'tag.foo.bar "MyTag" {fb.content contains "this is a string"}'

Yield Returns:

  • (AST::Node, Array<AST::Node>)

    An AST node or array of AST nodes to be wrapped by a :statement_scope node, to be a child of the returned :tag node.

See Also:


378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/csdl/builder.rb', line 378

def tag_tree(tag_namespaces, tag_class, &block)
  tag_namespace_nodes = tag_namespaces.map do |tag_namespace|
    s(:tag_namespace, tag_namespace)
  end

  s(:tag,
    s(:tag_namespaces,
      *tag_namespace_nodes),
    s(:tag_class,
      s(:string, tag_class)),
    statement_scope(&block))
end