source_proc 0.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/LICENSE +15 -0
- data/lib/source_proc.rb +131 -0
- data/test/source_proc_test.rb +61 -0
- metadata +56 -0
data/LICENSE
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Copyright (c) 2007 Samuel Lebeau
|
2
|
+
|
3
|
+
This program is free software; you can redistribute it and/or modify
|
4
|
+
it under the terms of the GNU General Public License as published by
|
5
|
+
the Free Software Foundation; either version 2 of the License, or
|
6
|
+
(at your option) any later version.
|
7
|
+
|
8
|
+
This program is distributed in the hope that it will be useful,
|
9
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
GNU General Public License for more details.
|
12
|
+
|
13
|
+
You should have received a copy of the GNU General Public License
|
14
|
+
along with this program; if not, write to the Free Software
|
15
|
+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
data/lib/source_proc.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'binding_of_caller'
|
4
|
+
|
5
|
+
# SourceProc objects are objects acting like Proc ones that can return
|
6
|
+
# back their own source code.
|
7
|
+
# They have to be created with a source string instead of a block, then
|
8
|
+
# all proc method calls are delegated to an internal Proc object created
|
9
|
+
# by evaluating this string.
|
10
|
+
#
|
11
|
+
# == Creation
|
12
|
+
#
|
13
|
+
# SourceProc objects can be created 2 ways:
|
14
|
+
# add = SourceProc.new('|a, b| a + b')
|
15
|
+
# or:
|
16
|
+
# add = slambda '|a, b| a + b'
|
17
|
+
#
|
18
|
+
# The first form creates its internal proc with <tt>Proc.new</tt>,
|
19
|
+
# the second one with <tt>lambda,</tt> so Ruby semantics are preserved
|
20
|
+
# and differences between procs and lambdas are kept.
|
21
|
+
#
|
22
|
+
# == Usage
|
23
|
+
#
|
24
|
+
# add.call(3, 4) # => 7
|
25
|
+
# add.arity # => 2
|
26
|
+
# add.source # => '|a, b| a + b'
|
27
|
+
#
|
28
|
+
# Be careful, SourceProc isn't a subclass of Proc, so
|
29
|
+
# add.is_a?(Proc) # => false
|
30
|
+
#
|
31
|
+
# Better way is to test if we can invoke 'call' on it
|
32
|
+
# add.respond_to(:call) # => true
|
33
|
+
#
|
34
|
+
# == Bindings
|
35
|
+
#
|
36
|
+
# When created inside a method, SourceProc objects will be given the current binding.
|
37
|
+
# For instance:
|
38
|
+
#
|
39
|
+
# str = 'Hello'
|
40
|
+
#
|
41
|
+
# def foo
|
42
|
+
# slambda '|arg| str + arg'
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# foo.call(' world !') # => 'Hello world !'
|
46
|
+
# eval('str', foo.binding) # => 'Hello'
|
47
|
+
#
|
48
|
+
# Due to the implementation of Binding.of_caller, the current binding will not be
|
49
|
+
# given if SourceProc objects are created outside a method.
|
50
|
+
# You can however pass a Binding object as second argument which will be used instead.
|
51
|
+
#
|
52
|
+
# Example:
|
53
|
+
#
|
54
|
+
# str = 'Hello'
|
55
|
+
#
|
56
|
+
# my_proc = slambda '|arg| str + arg'
|
57
|
+
# my_proc.call(' world !') # => Error
|
58
|
+
#
|
59
|
+
# # will give what is expected
|
60
|
+
# my_proc = slambda '|arg| str + arg', binding
|
61
|
+
# my_proc.call(' world !') # => 'Hello world !'
|
62
|
+
#
|
63
|
+
# == Marshalling
|
64
|
+
#
|
65
|
+
# You can marshall SourceProc objects and restore them back,
|
66
|
+
# except all bindings will be lost as Binding objects cannot be marshalled.
|
67
|
+
# This could however be usefull if you restore them in the same context
|
68
|
+
# you marshalled them.
|
69
|
+
|
70
|
+
class SourceProc
|
71
|
+
extend Forwardable
|
72
|
+
|
73
|
+
def SourceProc.new(source, binding = nil, is_lambda = false)
|
74
|
+
return super if binding
|
75
|
+
Binding.of_caller { |binding| super(source, binding, is_lambda) }
|
76
|
+
end
|
77
|
+
|
78
|
+
def SourceProc._load(string)
|
79
|
+
# obviously loosing the original binding
|
80
|
+
eval(string)
|
81
|
+
end
|
82
|
+
|
83
|
+
def initialize(source, binding, is_lambda)
|
84
|
+
@source, @is_lambda = source.freeze, is_lambda.freeze
|
85
|
+
@proc = eval(expression, binding)
|
86
|
+
end
|
87
|
+
|
88
|
+
def_delegators :@proc, :to_proc, :arity, :binding, :call, :[], :==
|
89
|
+
|
90
|
+
attr_reader :source
|
91
|
+
|
92
|
+
def lambda?
|
93
|
+
@is_lambda
|
94
|
+
end
|
95
|
+
|
96
|
+
def proc?
|
97
|
+
!@is_lambda
|
98
|
+
end
|
99
|
+
|
100
|
+
def dup
|
101
|
+
self.class.new(@source, @proc.binding, @is_lambda)
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_s(*args)
|
105
|
+
(@is_lambda ? 'slambda(' : 'SourceProc.new(') << source.inspect << ')'
|
106
|
+
end
|
107
|
+
alias _dump to_s
|
108
|
+
|
109
|
+
private
|
110
|
+
def method_missing?(meth_id, *args, &block)
|
111
|
+
@proc.respond_to?(meth_id) ? @proc.send(meth_id, *args, &block) : super
|
112
|
+
end
|
113
|
+
|
114
|
+
def expression
|
115
|
+
(@is_lambda ? 'lambda ' : 'Proc.new ') << source_with_braces
|
116
|
+
end
|
117
|
+
|
118
|
+
def source_with_braces
|
119
|
+
@source.index("\n") ? "do #@source\nend" : "{ #@source }"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
module Kernel
|
124
|
+
# convenient SourceProc creator for lambdas
|
125
|
+
def slambda(source, binding = nil)
|
126
|
+
return SourceProc.new(source, binding, true) if binding
|
127
|
+
Binding.of_caller { |binding| SourceProc.new(source, binding, true) }
|
128
|
+
end
|
129
|
+
# deprecated
|
130
|
+
alias sproc slambda
|
131
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'source_proc'
|
3
|
+
|
4
|
+
class SourceProcTest < Test::Unit::TestCase
|
5
|
+
def max_source
|
6
|
+
'a > b ? a : b'
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_creation
|
10
|
+
src = "|a, b| #{max_source}"
|
11
|
+
max = SourceProc.new(src)
|
12
|
+
max2 = slambda(src)
|
13
|
+
assert_equal(src, max.source)
|
14
|
+
assert_equal(src, max2.source)
|
15
|
+
assert(max2.lambda?)
|
16
|
+
assert_equal(4, max.call(3, 4))
|
17
|
+
assert_equal(4, max2.call(3, 4))
|
18
|
+
assert_nothing_raised(ArgumentError) { assert_equal(4, max.call(3, 4, 5)) }
|
19
|
+
assert_raise(ArgumentError) { max2.call(3, 4, 5) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def remote_call(proc)
|
23
|
+
proc.call
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_bindings
|
27
|
+
a, b = 128, 5
|
28
|
+
max = slambda(max_source)
|
29
|
+
assert_equal(128, max.call)
|
30
|
+
assert_equal(128, remote_call(max))
|
31
|
+
array = []
|
32
|
+
push = slambda 'array.push(1664)'
|
33
|
+
push.call
|
34
|
+
assert_equal(1, array.size)
|
35
|
+
eval('array.push(0 + 0)', push.binding)
|
36
|
+
assert_equal(2, array.size)
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_marshalling
|
40
|
+
f = slambda('|x, y| x + y')
|
41
|
+
g = SourceProc.new('ENV')
|
42
|
+
assert_nothing_raised do
|
43
|
+
f = Marshal.load(Marshal.dump(f))
|
44
|
+
g = Marshal.load(Marshal.dump(g))
|
45
|
+
end
|
46
|
+
assert_kind_of(SourceProc, f)
|
47
|
+
assert(f.lambda?)
|
48
|
+
assert(!g.lambda?)
|
49
|
+
assert_equal('|x, y| x + y', f.source)
|
50
|
+
assert_equal(2020, f.call(2012, 8))
|
51
|
+
assert_equal(ENV, g.call)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_dup
|
55
|
+
f, g = slambda('|x| x'), nil
|
56
|
+
assert_nothing_raised(Exception) { g = f.dup }
|
57
|
+
assert_equal(f.source, g.source)
|
58
|
+
assert_equal(g.call(true))
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: source_proc
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.1"
|
7
|
+
date: 2007-08-08 00:00:00 +02:00
|
8
|
+
summary: Proc objects that can give back their source code.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: sam@gotfresh.info
|
12
|
+
homepage: http://i.gotfresh.info
|
13
|
+
rubyforge_project:
|
14
|
+
description: SourceProc objects are created with a source string and binding and delegate calls to the corresponding Proc object letting you access to the original source code.
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Samuel Lebeau
|
31
|
+
files:
|
32
|
+
- LICENSE
|
33
|
+
- lib/source_proc.rb
|
34
|
+
- test/source_proc_test.rb
|
35
|
+
test_files: []
|
36
|
+
|
37
|
+
rdoc_options: []
|
38
|
+
|
39
|
+
extra_rdoc_files: []
|
40
|
+
|
41
|
+
executables: []
|
42
|
+
|
43
|
+
extensions: []
|
44
|
+
|
45
|
+
requirements: []
|
46
|
+
|
47
|
+
dependencies:
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: call_stack
|
50
|
+
version_requirement:
|
51
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 0.1.0.0
|
56
|
+
version:
|