relaxo-query-server 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -23,6 +23,12 @@ Edit your etc/couchdb/local.ini and add the following:
23
23
 
24
24
  Make sure the `relaxo-query-server` executable is accessible from `$PATH`.
25
25
 
26
+ `relaxo-query-server` includes a variety of 3rd party modules by default, but it can include other libraries by specifying them on the command line:
27
+
28
+ relaxo-ruby = ruby1.9 -rdate -rbigdecimal relaxo-query-server
29
+
30
+ (You can also load code by specifying libraries in your design documents and views.)
31
+
26
32
  To build and install the gem from source:
27
33
 
28
34
  cd build/
@@ -4,7 +4,7 @@ require 'relaxo/query_server'
4
4
  require 'optparse'
5
5
 
6
6
  OPTIONS = {
7
- :safe => 4,
7
+ :safe => 2
8
8
  }
9
9
 
10
10
  ARGV.options do |o|
@@ -21,10 +21,6 @@
21
21
  require 'relaxo/query_server/context'
22
22
  require 'relaxo/query_server/shell'
23
23
 
24
- # Provide useful standard functionality:
25
- require 'bigdecimal'
26
- require 'date'
27
-
28
24
  module Relaxo
29
25
  module QueryServer
30
26
  # Run the query server using `$stdin` and `$stdout` for communication.
@@ -34,7 +34,7 @@ module Relaxo
34
34
  def parse_function(text, scope, filename = 'query-server')
35
35
  safe_level = @options[:safe] || 0
36
36
 
37
- function = lambda { $SAFE = safe_level; eval(text, scope.send(:binding), filename) }.call
37
+ function = lambda { $SAFE = safe_level; eval(text, scope, filename) }.call
38
38
 
39
39
  unless function.respond_to? :call
40
40
  raise CompilationError.new('Expression does not evaluate to procedure!')
@@ -68,6 +68,8 @@ module Relaxo
68
68
  # ** Map functionality
69
69
  when 'add_fun'
70
70
  @mapper.add_function command[1]; true
71
+ when 'add_lib'
72
+ @mapper.add_libraries command[1]; true
71
73
  when 'map_doc'
72
74
  @mapper.map command[1]
73
75
  when 'reset'
@@ -176,7 +176,7 @@ module Relaxo
176
176
 
177
177
  # Implements the `validates_doc_update` action.
178
178
  def validates(function, new_document, old_document, user_context)
179
- ValidationProcess.new(@context, function).run(new_document, old_document, user_context)
179
+ Process.new(@context, function).run(new_document, old_document, user_context)
180
180
 
181
181
  # Unless ValidationError was raised, we are okay.
182
182
  return VALIDATED
@@ -193,7 +193,7 @@ module Relaxo
193
193
 
194
194
  # Looks a up a function given a key path into the design document.
195
195
  def function_for(path)
196
- parent = self
196
+ parent = @attributes
197
197
 
198
198
  function = path.inject(parent) do |current, key|
199
199
  parent = current
@@ -205,7 +205,7 @@ module Relaxo
205
205
 
206
206
  # Compile the function if required:
207
207
  if String === function
208
- parent[path.last] = @context.parse_function(function, self, 'design-document')
208
+ parent[path.last] = @context.parse_function(function, binding, 'design-document')
209
209
  else
210
210
  function
211
211
  end
@@ -0,0 +1,80 @@
1
+ # Copyright (c) 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module Relaxo
22
+ module QueryServer
23
+ class Library
24
+ def self.for(root, path)
25
+ parent = root
26
+
27
+ path = path.split('/') unless Array === path
28
+
29
+ library = path.inject(parent) do |current, name|
30
+ return nil if current == nil
31
+
32
+ parent = current
33
+
34
+ current[name]
35
+ end
36
+
37
+ return nil if library == nil
38
+
39
+ unless Library === library
40
+ library = parent[path.last] = Library.new(root, path, library)
41
+ end
42
+
43
+ return library.instance
44
+ end
45
+
46
+ def initialize(root, path, code)
47
+ @root = root
48
+ @path = path
49
+ @code = code
50
+
51
+ @klass = nil
52
+ @instance = nil
53
+ end
54
+
55
+ def instance
56
+ unless @klass
57
+ @klass = Class.new
58
+
59
+ # Not sure if this is the best way to implement the `load` function
60
+ @klass.const_set('ROOT', @root)
61
+ @klass.class_exec(@root) do |root|
62
+ @@root = root
63
+
64
+ def self.load(path)
65
+ Library.for(@@root, path)
66
+ end
67
+
68
+ def load(path)
69
+ self.class.load(path)
70
+ end
71
+ end
72
+
73
+ @klass.class_eval @code, @path.join('/')
74
+ end
75
+
76
+ @instance ||= @klass.new
77
+ end
78
+ end
79
+ end
80
+ end
@@ -19,13 +19,16 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require 'relaxo/query_server/process'
22
+ require 'relaxo/query_server/library'
22
23
 
23
24
  module Relaxo
24
25
  module QueryServer
25
26
  # Supports `Mapper` by providing a context with an `emit` method that collects the results from the mapping function.
26
27
  class MappingProcess < Process
27
- def initialize(context, function)
28
+ def initialize(context, mapper, function)
28
29
  super(context, function)
30
+
31
+ @mapper = mapper
29
32
  @results = []
30
33
  end
31
34
 
@@ -34,6 +37,10 @@ module Relaxo
34
37
  @results << [key, value]
35
38
  end
36
39
 
40
+ def load(name)
41
+ @mapper.load(name)
42
+ end
43
+
37
44
  def run(*args)
38
45
  begin
39
46
  call(*args)
@@ -47,20 +54,36 @@ module Relaxo
47
54
  end
48
55
 
49
56
  class Mapper
57
+ # The default library name
58
+ DEFAULT = ['_default']
59
+
50
60
  def initialize(context)
51
61
  @context = context
52
62
  @functions = []
63
+
64
+ @libraries = {}
53
65
  end
54
66
 
55
67
  # Adds a function by parsing the text, typically containing a textual representation of a lambda.
56
68
  def add_function text
57
- @functions << @context.parse_function(text, self)
69
+ @functions << @context.parse_function(text, binding)
70
+ end
71
+
72
+ def load(path)
73
+ Library.for(@libraries, path)
74
+ end
75
+
76
+ def add_libraries libraries
77
+ @libraries = libraries
58
78
  end
59
79
 
60
80
  # Map a document to a set of results by appling all functions.
61
81
  def map(document)
82
+ # Force load the default library:
83
+ load(DEFAULT)
84
+
62
85
  @functions.map do |function|
63
- MappingProcess.new(@context, function).run(document)
86
+ MappingProcess.new(@context, @libraries, function).run(document)
64
87
  end
65
88
  end
66
89
  end
@@ -54,7 +54,7 @@ module Relaxo
54
54
  # A composite list of items.
55
55
  def reduce(functions, items)
56
56
  functions = functions.collect do |function_text|
57
- @context.parse_function(function_text, self)
57
+ @context.parse_function(function_text, binding)
58
58
  end
59
59
 
60
60
  keys, values = [], []
@@ -80,7 +80,7 @@ module Relaxo
80
80
  # An array of values to reduce
81
81
  def rereduce(functions, values)
82
82
  functions = functions.collect do |function_text|
83
- @context.parse_function(function_text, self)
83
+ @context.parse_function(function_text, binding)
84
84
  end
85
85
 
86
86
  result = functions.map do |function|
@@ -23,7 +23,7 @@ module Relaxo
23
23
  module VERSION
24
24
  MAJOR = 0
25
25
  MINOR = 1
26
- TINY = 0
26
+ TINY = 1
27
27
 
28
28
  STRING = [MAJOR, MINOR, TINY].join('.')
29
29
  end
@@ -17,7 +17,7 @@ class ContextTest < Test::Unit::TestCase
17
17
  end
18
18
 
19
19
  def setup
20
- setup_context :safe => 0
20
+ setup_context :safe => 2
21
21
  end
22
22
 
23
23
  def teardown
@@ -0,0 +1,59 @@
1
+
2
+ require 'context_test'
3
+ require 'relaxo/query_server/library'
4
+
5
+ class LibrariesTest < ContextTest
6
+ def test_libraries
7
+ root = {
8
+ 'lib' => {
9
+ 'all' => %q{
10
+ require 'bigdecimal'
11
+ require 'bigdecimal/util'
12
+ },
13
+ 'bar' => %q{
14
+ load('lib/all')
15
+
16
+ def bar
17
+ 10.to_d
18
+ end
19
+ }
20
+ }
21
+ }
22
+
23
+ object = Relaxo::QueryServer::Library.for(root, 'lib/bar')
24
+
25
+ assert_not_nil object
26
+ assert object.respond_to? :bar
27
+
28
+ assert_equal 10.to_d, object.bar
29
+
30
+ # For efficiency, the same object is returned both times
31
+ same_object = Relaxo::QueryServer::Library.for(root, 'lib/bar')
32
+ assert_equal object, same_object
33
+ end
34
+
35
+ def test_map_reduce_libraries
36
+ library_code = %q{
37
+ def check_bar(doc)
38
+ yield doc if doc['bar'] == true
39
+ end
40
+ }
41
+
42
+ map_function_code = %q{
43
+ foo = load('foo')
44
+
45
+ lambda {|doc|
46
+ foo.check_bar(doc) {emit(true)}
47
+ }
48
+ }
49
+
50
+ response = @context.run ['add_lib', {'foo' => library_code}]
51
+ assert_equal true, response
52
+
53
+ response = @context.run ['add_fun', map_function_code]
54
+ assert_equal true, response
55
+
56
+ response = @context.run ['map_doc', {'bar' => true}]
57
+ assert_equal [[[true, nil]]], response
58
+ end
59
+ end
@@ -14,7 +14,7 @@ class ValidationsTest < ContextTest
14
14
  }
15
15
 
16
16
  response = @context.run ['ddoc', 'test', ['validate_doc_update'], [{'good' => true}, {'good' => true}, {}]]
17
- assert_equal true, response
17
+ assert_equal 1, response
18
18
 
19
19
  response = @context.run ['ddoc', 'test', ['validate_doc_update'], [{'bad' => true}, {'good' => true}, {}]]
20
20
  assert_equal({'forbidden' => 'bad'}, response)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relaxo-query-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -53,6 +53,7 @@ files:
53
53
  - bin/relaxo-query-server
54
54
  - lib/relaxo/query_server/context.rb
55
55
  - lib/relaxo/query_server/designer.rb
56
+ - lib/relaxo/query_server/library.rb
56
57
  - lib/relaxo/query_server/mapper.rb
57
58
  - lib/relaxo/query_server/process.rb
58
59
  - lib/relaxo/query_server/reducer.rb
@@ -61,6 +62,7 @@ files:
61
62
  - lib/relaxo/query_server.rb
62
63
  - test/context_test.rb
63
64
  - test/test_filters.rb
65
+ - test/test_libraries.rb
64
66
  - test/test_lists.rb
65
67
  - test/test_map.rb
66
68
  - test/test_reduce.rb