relaxo-query-server 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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