amalgalite 0.10.1-x86-mingw32
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/HISTORY +201 -0
- data/LICENSE +29 -0
- data/README +51 -0
- data/bin/amalgalite-pack +126 -0
- data/examples/a.rb +9 -0
- data/examples/blob.rb +88 -0
- data/examples/bootstrap.rb +36 -0
- data/examples/define_aggregate.rb +75 -0
- data/examples/define_function.rb +104 -0
- data/examples/gem-db.rb +94 -0
- data/examples/gems.db +0 -0
- data/examples/require_me.rb +11 -0
- data/examples/requires.rb +42 -0
- data/examples/schema-info.rb +34 -0
- data/ext/amalgalite/amalgalite3.c +290 -0
- data/ext/amalgalite/amalgalite3.h +151 -0
- data/ext/amalgalite/amalgalite3_blob.c +240 -0
- data/ext/amalgalite/amalgalite3_constants.c +221 -0
- data/ext/amalgalite/amalgalite3_database.c +1148 -0
- data/ext/amalgalite/amalgalite3_requires_bootstrap.c +210 -0
- data/ext/amalgalite/amalgalite3_statement.c +639 -0
- data/ext/amalgalite/extconf.rb +36 -0
- data/ext/amalgalite/gen_constants.rb +130 -0
- data/ext/amalgalite/sqlite3.c +106729 -0
- data/ext/amalgalite/sqlite3.h +5626 -0
- data/ext/amalgalite/sqlite3_options.h +4 -0
- data/ext/amalgalite/sqlite3ext.h +380 -0
- data/gemspec.rb +60 -0
- data/lib/amalgalite.rb +43 -0
- data/lib/amalgalite/1.8/amalgalite3.so +0 -0
- data/lib/amalgalite/1.9/amalgalite3.so +0 -0
- data/lib/amalgalite/aggregate.rb +67 -0
- data/lib/amalgalite/blob.rb +186 -0
- data/lib/amalgalite/boolean.rb +42 -0
- data/lib/amalgalite/busy_timeout.rb +47 -0
- data/lib/amalgalite/column.rb +97 -0
- data/lib/amalgalite/core_ext/kernel/require.rb +21 -0
- data/lib/amalgalite/database.rb +947 -0
- data/lib/amalgalite/function.rb +61 -0
- data/lib/amalgalite/index.rb +43 -0
- data/lib/amalgalite/packer.rb +226 -0
- data/lib/amalgalite/paths.rb +70 -0
- data/lib/amalgalite/profile_tap.rb +131 -0
- data/lib/amalgalite/progress_handler.rb +21 -0
- data/lib/amalgalite/requires.rb +120 -0
- data/lib/amalgalite/schema.rb +191 -0
- data/lib/amalgalite/sqlite3.rb +6 -0
- data/lib/amalgalite/sqlite3/constants.rb +80 -0
- data/lib/amalgalite/sqlite3/database/function.rb +48 -0
- data/lib/amalgalite/sqlite3/database/status.rb +68 -0
- data/lib/amalgalite/sqlite3/status.rb +60 -0
- data/lib/amalgalite/sqlite3/version.rb +37 -0
- data/lib/amalgalite/statement.rb +414 -0
- data/lib/amalgalite/table.rb +90 -0
- data/lib/amalgalite/taps.rb +2 -0
- data/lib/amalgalite/taps/console.rb +27 -0
- data/lib/amalgalite/taps/io.rb +71 -0
- data/lib/amalgalite/trace_tap.rb +35 -0
- data/lib/amalgalite/type_map.rb +63 -0
- data/lib/amalgalite/type_maps/default_map.rb +167 -0
- data/lib/amalgalite/type_maps/storage_map.rb +40 -0
- data/lib/amalgalite/type_maps/text_map.rb +22 -0
- data/lib/amalgalite/version.rb +37 -0
- data/lib/amalgalite/view.rb +26 -0
- data/spec/aggregate_spec.rb +169 -0
- data/spec/amalgalite_spec.rb +4 -0
- data/spec/blob_spec.rb +81 -0
- data/spec/boolean_spec.rb +23 -0
- data/spec/busy_handler.rb +165 -0
- data/spec/database_spec.rb +494 -0
- data/spec/default_map_spec.rb +87 -0
- data/spec/function_spec.rb +94 -0
- data/spec/integeration_spec.rb +111 -0
- data/spec/packer_spec.rb +60 -0
- data/spec/paths_spec.rb +28 -0
- data/spec/progress_handler_spec.rb +105 -0
- data/spec/requires_spec.rb +23 -0
- data/spec/rtree_spec.rb +71 -0
- data/spec/schema_spec.rb +120 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/sqlite3/constants_spec.rb +65 -0
- data/spec/sqlite3/database_status_spec.rb +36 -0
- data/spec/sqlite3/status_spec.rb +18 -0
- data/spec/sqlite3/version_spec.rb +14 -0
- data/spec/sqlite3_spec.rb +53 -0
- data/spec/statement_spec.rb +161 -0
- data/spec/storage_map_spec.rb +41 -0
- data/spec/tap_spec.rb +59 -0
- data/spec/text_map_spec.rb +23 -0
- data/spec/type_map_spec.rb +17 -0
- data/spec/version_spec.rb +15 -0
- data/tasks/announce.rake +43 -0
- data/tasks/config.rb +107 -0
- data/tasks/distribution.rake +77 -0
- data/tasks/documentation.rake +32 -0
- data/tasks/extension.rake +141 -0
- data/tasks/rspec.rake +33 -0
- data/tasks/rubyforge.rake +59 -0
- data/tasks/utils.rb +80 -0
- metadata +237 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module Amalgalite
|
2
|
+
##
|
3
|
+
# A base class for use in creating your own progress handler classes
|
4
|
+
#
|
5
|
+
class ProgressHandler
|
6
|
+
def to_proc
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
# the arity of the call method
|
11
|
+
def arity() 0 ; end
|
12
|
+
|
13
|
+
##
|
14
|
+
# Override this method, returning +false+ if the SQLite should act as if
|
15
|
+
# +interrupt!+ had been invoked.
|
16
|
+
#
|
17
|
+
def call
|
18
|
+
raise NotImplementedError, "The progress handler call() method must be implemented"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'amalgalite'
|
2
|
+
require 'pathname'
|
3
|
+
require 'zlib'
|
4
|
+
require 'amalgalite/packer'
|
5
|
+
|
6
|
+
module Amalgalite
|
7
|
+
#
|
8
|
+
# Requires encapsulates requiring items from the database
|
9
|
+
#
|
10
|
+
class Requires
|
11
|
+
class << self
|
12
|
+
def load_path_db_connections
|
13
|
+
@load_path_db_connections ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_path
|
17
|
+
@load_path ||= []
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Allocate a database connection to the given filename
|
22
|
+
#
|
23
|
+
def db_connection_to( dbfile_name )
|
24
|
+
unless connection = load_path_db_connections[ dbfile_name ]
|
25
|
+
connection = ::Amalgalite::Database.new( dbfile_name )
|
26
|
+
load_path_db_connections[dbfile_name] = connection
|
27
|
+
end
|
28
|
+
return connection
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Setting a class level variable as a flag to know what we are currently
|
33
|
+
# in the middle of requiring
|
34
|
+
#
|
35
|
+
def requiring
|
36
|
+
@requiring ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
def require( filename )
|
40
|
+
if load_path.empty? then
|
41
|
+
raise ::LoadError, "Amalgalite load path is empty -- #{filename}"
|
42
|
+
elsif $LOADED_FEATURES.include?( filename ) then
|
43
|
+
return false
|
44
|
+
elsif Requires.requiring.include?( filename ) then
|
45
|
+
return false
|
46
|
+
else
|
47
|
+
Requires.requiring << filename
|
48
|
+
load_path.each do |lp|
|
49
|
+
if lp.require( filename ) then
|
50
|
+
Requires.requiring.delete( filename )
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
Requires.requiring.delete( filename )
|
55
|
+
raise ::LoadError, "amalgalite has no such file to load -- #{filename}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_reader :dbfile_name
|
61
|
+
attr_reader :table_name
|
62
|
+
attr_reader :filename_column
|
63
|
+
attr_reader :contents_column
|
64
|
+
attr_reader :compressed_column
|
65
|
+
attr_reader :db_connection
|
66
|
+
|
67
|
+
def initialize( opts = {} )
|
68
|
+
@dbfile_name = opts[:dbfile_name] || Bootstrap::DEFAULT_DB
|
69
|
+
@table_name = opts[:table_name] || Bootstrap::DEFAULT_TABLE
|
70
|
+
@filename_column = opts[:filename_column] || Bootstrap::DEFAULT_FILENAME_COLUMN
|
71
|
+
@contents_column = opts[:contents_column] || Bootstrap::DEFAULT_CONTENTS_COLUMN
|
72
|
+
@compressed_column = opts[:compressed_column] || Bootstrap::DEFAULT_COMPRESSED_COLUMN
|
73
|
+
@db_connection = Requires.db_connection_to( dbfile_name )
|
74
|
+
Requires.load_path << self
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# return the sql to find the file contents for a file in this requires
|
79
|
+
#
|
80
|
+
def sql
|
81
|
+
@sql ||= "SELECT #{filename_column}, #{compressed_column}, #{contents_column} FROM #{table_name} WHERE #{filename_column} = ?"
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# load a file in this database table. This will check and see if the
|
86
|
+
# file is already required. If it isn't it will select the contents
|
87
|
+
# associated with the row identified by the filename and eval those contents
|
88
|
+
# within the context of TOPLEVEL_BINDING. The filename is then appended to
|
89
|
+
# $LOADED_FEATURES.
|
90
|
+
#
|
91
|
+
# if the file was required then true is returned, otherwise false
|
92
|
+
#
|
93
|
+
def require( filename )
|
94
|
+
if $LOADED_FEATURES.include?( filename ) then
|
95
|
+
return false
|
96
|
+
else
|
97
|
+
begin
|
98
|
+
filename = filename.gsub(/\.rb\Z/,'')
|
99
|
+
rows = db_connection.execute(sql, filename)
|
100
|
+
if rows.size > 0 then
|
101
|
+
row = rows.first
|
102
|
+
contents = row[contents_column].to_s
|
103
|
+
if row[compressed_column] then
|
104
|
+
contents = ::Amalgalite::Packer.gunzip( contents )
|
105
|
+
end
|
106
|
+
|
107
|
+
eval( contents, TOPLEVEL_BINDING, row[filename_column] )
|
108
|
+
$LOADED_FEATURES << row[filename_column]
|
109
|
+
return true
|
110
|
+
else
|
111
|
+
return false
|
112
|
+
end
|
113
|
+
rescue => e
|
114
|
+
raise ::LoadError, "Failure loading #{filename} from #{dbfile_name} : #{e}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
require 'amalgalite/core_ext/kernel/require'
|
@@ -0,0 +1,191 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'amalgalite/table'
|
7
|
+
require 'amalgalite/index'
|
8
|
+
require 'amalgalite/column'
|
9
|
+
require 'amalgalite/view'
|
10
|
+
|
11
|
+
module Amalgalite
|
12
|
+
#
|
13
|
+
# An object view of the schema in the SQLite database. If the schema changes
|
14
|
+
# after this class is created, it has no knowledge of that.
|
15
|
+
#
|
16
|
+
class Schema
|
17
|
+
|
18
|
+
attr_reader :catalog
|
19
|
+
attr_reader :schema
|
20
|
+
attr_writer :dirty
|
21
|
+
attr_reader :db
|
22
|
+
|
23
|
+
#
|
24
|
+
# Create a new instance of Schema
|
25
|
+
#
|
26
|
+
def initialize( db, catalog = 'main', schema = 'sqlite')
|
27
|
+
@db = db
|
28
|
+
@catalog = catalog
|
29
|
+
@schema = schema
|
30
|
+
@tables = {}
|
31
|
+
@views = {}
|
32
|
+
@dirty = true
|
33
|
+
load_schema!
|
34
|
+
end
|
35
|
+
|
36
|
+
def dirty?() @dirty; end
|
37
|
+
def dirty!() @dirty = true; end
|
38
|
+
|
39
|
+
#
|
40
|
+
# load the schema from the database
|
41
|
+
def load_schema!
|
42
|
+
load_tables
|
43
|
+
load_views
|
44
|
+
@dirty = false
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# return the tables, reloading if dirty
|
49
|
+
def tables
|
50
|
+
load_schema! if dirty?
|
51
|
+
return @tables
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# load all the tables
|
56
|
+
#
|
57
|
+
def load_tables
|
58
|
+
@tables = {}
|
59
|
+
@db.execute("SELECT tbl_name FROM sqlite_master WHERE type = 'table' AND name != 'sqlite_sequence'") do |table_info|
|
60
|
+
table = load_table( table_info['tbl_name'] )
|
61
|
+
table.indexes = load_indexes( table )
|
62
|
+
@tables[table.name] = table
|
63
|
+
end
|
64
|
+
return @tables
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Load a single table
|
69
|
+
def load_table( table_name )
|
70
|
+
rows = @db.execute("SELECT tbl_name, sql FROM sqlite_master WHERE type = 'table' AND tbl_name = ?", table_name)
|
71
|
+
table_info = rows.first
|
72
|
+
table = nil
|
73
|
+
if table_info then
|
74
|
+
table = Amalgalite::Table.new( table_info['tbl_name'], table_info['sql'] )
|
75
|
+
table.columns = load_columns( table )
|
76
|
+
table.schema = self
|
77
|
+
table.indexes = load_indexes( table )
|
78
|
+
@tables[table.name] = table
|
79
|
+
else
|
80
|
+
# might be a temporary table
|
81
|
+
table = Amalgalite::Table.new( table_name, nil )
|
82
|
+
cols = load_columns( table )
|
83
|
+
if cols.size > 0 then
|
84
|
+
table.columns = cols
|
85
|
+
table.schema = self
|
86
|
+
table.indexes = load_indexes( table )
|
87
|
+
@tables[table.name] = table
|
88
|
+
end
|
89
|
+
end
|
90
|
+
return table
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# load all the indexes for a particular table
|
95
|
+
#
|
96
|
+
def load_indexes( table )
|
97
|
+
indexes = {}
|
98
|
+
|
99
|
+
@db.prepare("SELECT name, sql FROM sqlite_master WHERE type ='index' and tbl_name = $name") do |idx_stmt|
|
100
|
+
idx_stmt.execute( "$name" => table.name) do |idx_info|
|
101
|
+
indexes[idx_info['name']] = Amalgalite::Index.new( idx_info['name'], idx_info['sql'], table )
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
@db.execute("PRAGMA index_list( #{@db.quote(table.name)} );") do |idx_list|
|
106
|
+
idx = indexes[idx_list['name']]
|
107
|
+
|
108
|
+
# temporary indexes do not show up in the previous list
|
109
|
+
if idx.nil? then
|
110
|
+
idx = Amalgalite::Index.new( idx_list['name'], nil, table )
|
111
|
+
indexes[idx_list['name']] = idx
|
112
|
+
end
|
113
|
+
|
114
|
+
idx.sequence_number = idx_list['seq']
|
115
|
+
idx.unique = Boolean.to_bool( idx_list['unique'] )
|
116
|
+
|
117
|
+
@db.execute("PRAGMA index_info( #{@db.quote(idx.name)} );") do |col_info|
|
118
|
+
idx.columns << table.columns[col_info['name']]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
return indexes
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# load all the columns for a particular table
|
126
|
+
#
|
127
|
+
def load_columns( table )
|
128
|
+
cols = {}
|
129
|
+
idx = 0
|
130
|
+
@db.execute("PRAGMA table_info(#{@db.quote(table.name)})") do |row|
|
131
|
+
col = Amalgalite::Column.new( "main", table.name, row['name'], row['cid'])
|
132
|
+
|
133
|
+
col.default_value = row['dflt_value']
|
134
|
+
|
135
|
+
col.declared_data_type = row['type']
|
136
|
+
col.not_null_constraint = row['notnull']
|
137
|
+
col.primary_key = row['pk']
|
138
|
+
|
139
|
+
# need to remove leading and trailing ' or " from the default value
|
140
|
+
if col.default_value and col.default_value.kind_of?( String ) and ( col.default_value.length >= 2 ) then
|
141
|
+
fc = col.default_value[0].chr
|
142
|
+
lc = col.default_value[-1].chr
|
143
|
+
if fc == lc and ( fc == "'" || fc == '"' ) then
|
144
|
+
col.default_value = col.default_value[1..-2]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
unless table.temporary? then
|
149
|
+
# get more exact information
|
150
|
+
@db.api.table_column_metadata( "main", table.name, col.name ).each_pair do |key, value|
|
151
|
+
col.send("#{key}=", value)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
col.schema = self
|
155
|
+
cols[col.name] = col
|
156
|
+
idx += 1
|
157
|
+
end
|
158
|
+
return cols
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# return the views, reloading if dirty
|
163
|
+
#
|
164
|
+
def views
|
165
|
+
reload_schema! if dirty?
|
166
|
+
return @views
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# load a single view
|
171
|
+
#
|
172
|
+
def load_view( name )
|
173
|
+
rows = @db.execute("SELECT name, sql FROM sqlite_master WHERE type = 'view' AND name = ?", name )
|
174
|
+
view_info = rows.first
|
175
|
+
view = Amalgalite::View.new( view_info['name'], view_info['sql'] )
|
176
|
+
view.schema = self
|
177
|
+
return view
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# load all the views for the database
|
182
|
+
#
|
183
|
+
def load_views
|
184
|
+
@db.execute("SELECT name, sql FROM sqlite_master WHERE type = 'view'") do |view_info|
|
185
|
+
view = load_view( view_info['name'] )
|
186
|
+
@views[view.name] = view
|
187
|
+
end
|
188
|
+
return @views
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. See LICENSE and/or COPYING for details.
|
4
|
+
#++
|
5
|
+
module Amalgalite::SQLite3
|
6
|
+
module Constants
|
7
|
+
module Helpers
|
8
|
+
#
|
9
|
+
# convert an integer value into the string representation of the associated
|
10
|
+
# constant. this is a helper method used by some of the other modules
|
11
|
+
#
|
12
|
+
def name_from_value( value )
|
13
|
+
unless defined? @const_map_from_value
|
14
|
+
@const_map_from_value = {}
|
15
|
+
constants.each do |const_name|
|
16
|
+
c_int = const_get( const_name )
|
17
|
+
@const_map_from_value[c_int] = const_name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
return @const_map_from_value[ value ]
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# convert a string into the constant value. This is helper method used by
|
25
|
+
# some of the other modules
|
26
|
+
#
|
27
|
+
def value_from_name( name )
|
28
|
+
unless defined? @const_map_from_name
|
29
|
+
@const_map_from_name = {}
|
30
|
+
constants.each do |const_name|
|
31
|
+
c_int = const_get( const_name )
|
32
|
+
@const_map_from_name[ const_name ] = c_int
|
33
|
+
end
|
34
|
+
end
|
35
|
+
return @const_map_from_name[ name.upcase ]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
##
|
41
|
+
# DataType defines the namespace for all possible SQLite data types.
|
42
|
+
#
|
43
|
+
module DataType
|
44
|
+
end
|
45
|
+
DataType.freeze
|
46
|
+
|
47
|
+
##
|
48
|
+
# Open defines the namespace for all possible flags to the Database.open
|
49
|
+
# method
|
50
|
+
#
|
51
|
+
module Open
|
52
|
+
end
|
53
|
+
Open.freeze
|
54
|
+
|
55
|
+
##
|
56
|
+
# Status defines the namespace for all the possible status flags for
|
57
|
+
# Amalgalite::SQLite3::Status objects
|
58
|
+
#
|
59
|
+
module Status
|
60
|
+
extend Helpers
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
##
|
65
|
+
# DBStatus defines the namespace for all the possible status codes for the
|
66
|
+
# Amalgalite::SQlite3::Database::Status objects.
|
67
|
+
#
|
68
|
+
module DBStatus
|
69
|
+
extend Helpers
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# ResultCode defines the namespace for all possible result codes from an
|
74
|
+
# SQLite API call.
|
75
|
+
#
|
76
|
+
module ResultCode
|
77
|
+
extend Helpers
|
78
|
+
end # end ResultCode
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Amalgalite::SQLite3
|
2
|
+
class Database
|
3
|
+
##
|
4
|
+
# A wrapper around a proc for use as an SQLite Ddatabase fuction
|
5
|
+
#
|
6
|
+
# f = Function.new( 'md5', lambda { |x| Digest::MD5.hexdigest( x.to_s ) } )
|
7
|
+
#
|
8
|
+
class Function
|
9
|
+
|
10
|
+
# the name of the function, and how it will be called in SQL
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# The unique signature of this function. This is used to determin if the
|
14
|
+
# function is already registered or not
|
15
|
+
#
|
16
|
+
def self.signature( name, arity )
|
17
|
+
"#{name}/#{arity}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Initialize with the name and the Proc
|
21
|
+
#
|
22
|
+
def initialize( name, _proc )
|
23
|
+
@name = name
|
24
|
+
@function = _proc
|
25
|
+
end
|
26
|
+
|
27
|
+
# The unique signature of this function
|
28
|
+
#
|
29
|
+
def signature
|
30
|
+
@signature ||= Function.signature( name, arity )
|
31
|
+
end
|
32
|
+
alias :to_s :signature
|
33
|
+
|
34
|
+
# The arity of SQL function, -1 means it is takes a variable number of
|
35
|
+
# arguments.
|
36
|
+
#
|
37
|
+
def arity
|
38
|
+
@function.arity
|
39
|
+
end
|
40
|
+
|
41
|
+
# Invoke the proc
|
42
|
+
#
|
43
|
+
def call( *args )
|
44
|
+
@function.call( *args )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|