Module: Pacer::Core::Route

Included in:
PacerGraph, Route
Defined in:
lib/pacer/core/route.rb,
lib/pacer/transform/path.rb,
lib/pacer/transform/gather.rb,
lib/pacer/transform/branch.rb,
lib/pacer/transform/scatter.rb,
lib/pacer/filter/random_filter.rb,
lib/pacer/filter/collection_filter.rb

Overview

The basic internal logic for routes and core route shared methods are defined here. Many of these methods are designed to be specialized by other modules included after Core::Route is included.

Defined Under Namespace

Classes: DetachedPipe, DetachedRoute

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#hide_elementstrue, false

If true, elements won't be printed to STDOUT when #inspect is called on this route.

Returns:

  • (true, false)


27
28
29
# File 'lib/pacer/core/route.rb', line 27

def hide_elements
  @hide_elements
end

#infoObject

Returns the value of attribute info



19
20
21
# File 'lib/pacer/core/route.rb', line 19

def info
  @info
end

#lookahead_replacementObject

If a route's function won't do the expected thing in a lookahead, set a proc here that will correct the route. For instance:

g.v.lookahead { |v| v[:prop] }

gets transformed to:

g.v.lookahead { |v| v.property?(:prop) }


43
44
45
# File 'lib/pacer/core/route.rb', line 43

def lookahead_replacement
  @lookahead_replacement
end

#pipe_args[Object] #pipe_args=(args) ⇒ [Object] #pipe_args=(args) ⇒ [Object]

The arguments passed to the pipe constructor.

Overloads:

  • #pipe_args=(args) ⇒ [Object]

    Parameters:

    • args (Object)
  • #pipe_args=(args) ⇒ [Object]

    Parameters:

    • args ([Object])

Returns:

  • ([Object])

    array of arguments



73
74
75
# File 'lib/pacer/core/route.rb', line 73

def pipe_args
  @pipe_args
end

#pipe_classObject

Specify which pipe class will be instantiated when an iterator is created.



22
23
24
# File 'lib/pacer/core/route.rb', line 22

def pipe_class
  @pipe_class
end

#remove_from_lookaheadObject

If this piece of the route is useless in a lookahead, set this to true and when it is at the tail of a lookahead, it will be removed automatically. (for instance the property decoder, or wrap/unwrap)



32
33
34
# File 'lib/pacer/core/route.rb', line 32

def remove_from_lookahead
  @remove_from_lookahead
end

#route_nameObject

Replace the generated class name of this route with a specific one by setting route_name.



18
19
20
# File 'lib/pacer/core/route.rb', line 18

def route_name
  @route_name
end

Instance Method Details

#-(other) ⇒ Object



12
13
14
# File 'lib/pacer/filter/collection_filter.rb', line 12

def -(other)
  chain_route :filter => :collection, :except => other
end

#==(other) ⇒ true, false

Returns true if the other route is defined the same as the current route.

Note that block filters will prevent matches even with identically defined routes unless the routes are actually the same object.

Returns:

  • (true, false)


351
352
353
354
355
356
357
# File 'lib/pacer/core/route.rb', line 351

def ==(other)
  other.class == self.class and
    other.function == function and
    other.element_type == element_type and
    other.back == back and
    other.pacer_source == pacer_source
end

#add_extensions(exts) ⇒ self

If any objects in the given array are modules that contain a Route submodule, extend this route with the Route module.

Returns:

  • (self)


371
372
373
# File 'lib/pacer/core/route.rb', line 371

def add_extensions(exts)
  chain_route extensions: (extensions - exts) + exts
end

#branch(&block) ⇒ Object



4
5
6
7
# File 'lib/pacer/transform/branch.rb', line 4

def branch(&block)
  route = chain_route transform: ::Pacer::Transform::Branch
  route.add_block! &block
end

#chain_route(args_hash) ⇒ Object



60
61
62
# File 'lib/pacer/core/route.rb', line 60

def chain_route(args_hash)
  Pacer::RouteBuilder.current.chain self, args_hash
end

#cond(where, &block) ⇒ Object



9
10
11
12
# File 'lib/pacer/transform/branch.rb', line 9

def cond(where, &block)
  route = chain_route transform: ::Pacer::Transform::Branch
  route.add_cond! where, &block
end

#description(join = ' -> ') ⇒ Object



296
297
298
# File 'lib/pacer/core/route.rb', line 296

def description(join = ' -> ')
  "#<#{inspect_strings.join(join)}>"
end

#detachObject

Returns an outer proc that returns an inner proc that will execute the given route for a given input data or element.

The outer proc contains the already-compiled route (the most expensive part of the process). When you call it, it returns the inner proc. If you call it with no arguments, it will prepare the proc in the context of the original route used to create it. In a multi-tenant or multi-graph system, call the outer proc with the current graph to prepare so that it correctly wraps returned elements.

The inner proc contains a non-thread-safe data pipeline compiled from the route. Call it with an item of input data for the detached pipe and it will execute the pipeline for that piece of data and return the result or results.

A detached route can be configured with or without gather enabled. If enabled (default), the result will be an ArrayList of all result data produced by the detached route. If not, the result will be either the first thing produced by the route or null if nothing was produced.



396
397
398
399
# File 'lib/pacer/core/route.rb', line 396

def detach
  route = yield Pacer::Route.empty(self)
  DetachedRoute.new route
end

#each {|item| ... } ⇒ Enumerator

Iterates over each element or object resulting from traversing the route up to this point.

Yields:

  • (item)

    if a block is given

Returns:

  • (Enumerator)

    if no block is given



125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/pacer/core/route.rb', line 125

def each
  iter = iterator
  iter = configure_iterator(iter)
  if block_given?
    while true
      yield iter.next
    end
  else
    iter
  end
rescue Pacer::EmptyPipe, java.util.NoSuchElementException
  self
end

#empty?Boolean

Returns true if this route currently has no elements.

Returns:

  • (Boolean)


360
361
362
# File 'lib/pacer/core/route.rb', line 360

def empty?
  none?
end

#except(excluded) ⇒ Object



4
5
6
7
8
9
10
# File 'lib/pacer/filter/collection_filter.rb', line 4

def except(excluded)
  if excluded.is_a? Symbol
    chain_route :filter => :collection, :except_var => excluded
  else
    chain_route :filter => :collection, :except => excluded
  end
end

#from_graph?(g) ⇒ Boolean

Returns true if the given graph is the one this route operates on.

Returns:

  • (Boolean)


56
57
58
# File 'lib/pacer/core/route.rb', line 56

def from_graph?(g)
  graph.equals g
end

#gather(into = nil, &block) ⇒ Object



4
5
6
# File 'lib/pacer/transform/gather.rb', line 4

def gather(into = nil, &block)
  aggregate(into, &block).cap(element_type: :array)
end

#graphPacerGraph

Return which graph this route operates on.

Returns:



48
49
50
51
52
53
# File 'lib/pacer/core/route.rb', line 48

def graph
  config.fetch :graph do
    src = @back || @pacer_source
    src.graph if src.respond_to? :graph
  end
end

#help(section = nil) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/pacer/core/route.rb', line 154

def help(section = nil)
  general_topics = <<HELP
Some general help sections:
:routes     What are Pacer's routes?
:basics     Simple usage examples
:creation   How to create records
:tools      Some things you can do
:graphs     Available graphs
:help       How to contribute to Pacer's inline help
HELP
  if section == :help
    puts <<HELP
Contributions of help topics will be greatly apreciated.

Help topics should be added to the modules that they describe. General topics
help can be added here for now but will likely be moved eventually.

See lib/pacer/transform/map.rb for an example of how to define contextual help
topics.

If you add a general topic, remember to add it to the list above.

Remember to call super for unrecognized sections! :)

HELP
  elsif section == :routes
    puts <<HELP
Pacer's routes are a very efficient and fast way to deal with data.

The fundamental thing about them is that they are lazily evaluated, which
allows very expensive traversals to be defined, yet nearly always produces
results immediately with very low memory requirements, too.



HELP
  elsif section == :basics
    puts <<HELP
Pacer basics:

g = Pacer.tg                           # create an in-memory graph
                                 # - help(:graphs) for other types
g.v                                    # create a route to all vertices in the graph
                                 # vertices are your basic documents.
g.e                                    # ... or all edges
                                 # edges connect documents. They can have properties, too!
g.v(name: 'Sue', gender: 'male')       # find all boys named Sue
                                 # all elements are schemaless, so you
                                 # can specify any properties
g.v(name: 'Sue', gender: 'male').count # how many are there?
                                 # see how we can chain calls? Powerful
                                 # traversals can be defined this way.
g.v.out_e(:friend)                     # see Sue's 'friend' relationships
g.v.out_e(:friend).in_v                # continue on to his friends
g.v.out_e(:friend).in_v.out(:friends)  # continue on to his friends-of-friends
g.v.in(:friend)                        # see who has friended Sue.
                                 # all edges are directional to follow an
                                 # edge, use #out or #out_e; to go
                                 # backwards along an edge, use #in or
                                 # #in_e

#{general_topics}

HELP
  elsif section == :creation
    puts <<HELP
How we can create records in a Pacer graph:

g = Pacer.tg                          # create a new in-memory graph to play with
sue = g.create_vertex name: 'Sue', gender: 'male'
                                # create a record with properties
ghost = g.create_vertex               # get lazy and create an empty one
sue.add_edges_to :friend, ghost       # Sue has friended the ghost.
                                # This relationship can be traversed in
                                # both directions so a reverse
                                # relationship is *not* required, but can
                                # be created:
ghost.add_edges_to :friend, sue, type: 'spooky'
                                # We can also create edges with properties
sue[:age] = 27                        # It's that easy to add or change a property
sue['fav foods'] = [pie, donuts]      # Pacer can shoehorn any data into the
                                # graph as long as it's serializable.

#{general_topics}

HELP
  elsif section == :tools
    puts <<HELP

HELP
  elsif section == :graphs or section == :plugins
    puts <<HELP
Various graphs are supported in their own Rubygems. Check out pacer-neo4j,
pacer-orient, pacer-dex for now. New graphs emerge frequently and I hope to
support many of them.

Search rubygems.org or github for projects that start with "pacer-" to see what
other plugins exist as well.

#{general_topics}

HELP
  else
    if section
      puts "Unrecognized help section specified"
      puts
    elsif not is_a? Graph
      puts "No specialized help has been defined for this step yet."
      puts
    end
    puts <<HELP
How to use Pacer's inline help:

You can use Pacer.help(:section) to print help on general topics, or get
context-specific help by calling help on any route. For example:

    graph.v.out_e.map.help  # will give you help about the map command.

#{general_topics}

General options (may not be available for all methods)

  element_type: Symbol  Set what type of element is emitted from this step.
registered types: #{ Pacer::RouteBuilder.current.element_types.keys.map(&:inspect).join ', ' }

  graph: PacerGraph     If the route contains graph elements, specify that they
                  are from this graph

  route_name: String    Name for this route when inspecting it in IRB.

  info: String          Put what you want here. Appears when the route is inspected.

  extensions: [Module]  Extra extensions to add to the route.

  wrapper: Class         Wrap elements in this class.
For each element, wrapper.new(graph, element) happens

HELP
  end
  description
end

#inspect(limit = nil) ⇒ String

Returns a string representation of the route definition. If there are less than Graph#inspect_limit matches, it will also output all matching elements formatted in columns up to a maximum character width of Graph#columns. If this output behaviour is undesired, it may be turned off by calling #route on the current route.

Returns:

  • (String)


307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/pacer/core/route.rb', line 307

def inspect(limit = nil)
  if Pacer.hide_route_elements or hide_elements or source_iterator.nil?
    description
  else
    graph_read_transaction do
      Pacer.hide_route_elements do
        count = 0
        limit ||= Pacer.inspect_limit
        results = collect do |v|
          count += 1
          if count > limit
            puts "Total: > #{ limit } (Pacer.inspect_limit)"
            return route.inspect
          end
          v.inspect
        end
        if count > 0
          lens = results.collect { |r| r.length }
          max = lens.max
          cols = (Pacer.columns / (max + 1).to_f).floor
          cols = 1 if cols < 1
          if cols == 1
            template_part = ['%s']
          else
            template_part = ["%-#{max}s"]
          end
          template = (template_part * cols).join(' ')
          results.each_slice(cols) do |row|
            template = (template_part * row.count).join(' ') if row.count < cols
            puts template % row
          end
        end
        puts "Total: #{ count }"
        description
      end
    end
  end
end

#no_extensionsObject



379
380
381
# File 'lib/pacer/core/route.rb', line 379

def no_extensions
  chain_route(:extensions => [], :wrapper => nil)
end

#only(included) ⇒ Object



16
17
18
19
20
21
22
# File 'lib/pacer/filter/collection_filter.rb', line 16

def only(included)
  if included.is_a? Symbol
    chain_route :filter => :collection, :only_var => included
  else
    chain_route :filter => :collection, :only => included
  end
end

#paths(*exts) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
# File 'lib/pacer/transform/path.rb', line 4

def paths(*exts)
  route = chain_route :transform => :path, :element_type => :path
  if exts.any?
    exts = exts.map { |e| Array.wrap(e) if e }
    route.map(element_type: :path) do |path|
      path.zip(exts).map { |element, ext| ext ? element.add_extensions(ext) : element }
    end
  else
    route
  end
end

#pipe {|java.util.Iterator| ... } ⇒ java.util.Iterator

Returns a single use pipe iterator based on this route.

Yields:

  • (java.util.Iterator)

    the pipe. Very useful because this method will catch the pipe's java.util.NoSuchElementException when iteration is finished.

Returns:

  • (java.util.Iterator)

    the pipe.



143
144
145
146
147
148
149
150
151
152
# File 'lib/pacer/core/route.rb', line 143

def pipe
  iterator = each
  if block_given?
    yield iterator if block_given?
  else
    iterator
  end
rescue Pacer::EmptyPipe, java.util.NoSuchElementException
  nil
end

#random(bias = 0.5) ⇒ Object

Return elements based on a bias:1 chance.

If given an integer (n) > 0, bias is calcualated at 1 / n.



7
8
9
10
# File 'lib/pacer/filter/random_filter.rb', line 7

def random(bias = 0.5)
  bias = 1 / bias.to_f if bias.is_a? Fixnum and bias > 0
  chain_route :pipe_class => Pacer::Pipes::RandomFilterPipe, :pipe_args => bias
end

#root?Boolean

Return true if this route is at the beginning of the route definition.

Returns:

  • (Boolean)


84
85
86
# File 'lib/pacer/core/route.rb', line 84

def root?
  !@pacer_source.nil? or @back.nil?
end

#routeself

TODO:

rename this method

Prevents the route from being evaluated when it is inspected. Useful for computationally expensive or one-time routes.

Returns:

  • (self)


94
95
96
97
# File 'lib/pacer/core/route.rb', line 94

def route
  @hide_elements = true
  self
end

#scatter(args = {}) ⇒ Object



4
5
6
# File 'lib/pacer/transform/scatter.rb', line 4

def scatter(args = {})
  chain_route({transform: :scatter, element_type: :object}.merge(args))
end

#set_extensions(exts) ⇒ Object



375
376
377
# File 'lib/pacer/core/route.rb', line 375

def set_extensions(exts)
  chain_route extensions: exts, wrapper: nil
end

#set_pipe_source(src) ⇒ Object

Note:

all routes derived from any route in the chain will be affected so use with caution.

Change the source of this route.

Parameters:

  • src (Enumerable, Enumerator)

    the data source.



408
409
410
411
412
413
414
# File 'lib/pacer/core/route.rb', line 408

def set_pipe_source(src)
  if @back
    @back.set_pipe_source src
  else
    self.pacer_source = src
  end
end

#set_wrapper(wrapper) ⇒ Object



364
365
366
# File 'lib/pacer/core/route.rb', line 364

def set_wrapper(wrapper)
  chain_route wrapper: wrapper
end

#varsHash

TODO:

It would maybe be better if the vars were tied to the thread context or preferably to the actual pipe instance in use. The current implementation of vars is not threadsafe if the same route is being used in multiple threads concurrently.

Returns the hash of variables used during the previous evaluation of the route.

The contents of vars is expected to be in a state relevant to the latest route being evaluated and is primarily meant for internal use, but YMMV.

Returns:



112
113
114
115
116
117
118
# File 'lib/pacer/core/route.rb', line 112

def vars
  if @back
    @back.vars
  else
    @vars ||= {}
  end
end