svn 0.1.0

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.
@@ -0,0 +1,249 @@
1
+ require 'rubygems'
2
+ require 'ffi'
3
+
4
+ module Svn #:nodoc:
5
+
6
+ # Utility functions for working with FFI
7
+ module Utils
8
+
9
+ module_function
10
+
11
+ # Returns a pointer to the object_id of +obj+
12
+ def wrap( obj )
13
+ ptr = FFI::MemoryPointer.new( :uint64 )
14
+ ptr.write_uint64( obj.object_id )
15
+ ptr
16
+ end
17
+
18
+ # Returns the object for the object_id stored in +ptr+
19
+ def unwrap( ptr )
20
+ ObjectSpace._id2ref( ptr.read_uint64 )
21
+ end
22
+
23
+ # a generic factory class for use with FFI data types that adds default
24
+ # arguments to constructor calls
25
+ #
26
+ # # when NativeHash is created, the args are [ptr, :string, :string]
27
+ # bind :get_hash, :returning => NativeHash.factory( :string, :string )
28
+ class Factory
29
+
30
+ # factories also work as DataConverters so they can be used with FFI
31
+ include FFI::DataConverter
32
+
33
+ def initialize( klass, *args )
34
+ @klass = klass
35
+ @added_args = args
36
+ end
37
+
38
+ def new( *args )
39
+ @klass.new( *args, *@added_args )
40
+ end
41
+
42
+ def from_native( ptr, ctx )
43
+ @klass.new( ptr, *@added_args )
44
+ end
45
+
46
+ def native_type
47
+ @klass.native_type
48
+ end
49
+
50
+ def real_class
51
+ @klass
52
+ end
53
+
54
+ def size
55
+ @klass.size
56
+ end
57
+ end
58
+
59
+ # returns the *contents* of the pointer as type
60
+ #
61
+ # for example:
62
+ # # void get_string( char **p ):
63
+ # get_string( out_ptr ); content_for( out_ptr, :string )
64
+ #
65
+ # # void get_hash( hash_t **p ):
66
+ # class Hash < FFI::AutoPointer; end
67
+ # get_hash( out_ptr ); content_for( out_ptr, Hash )
68
+ #
69
+ # if the type is a FFI::Pointer, this will try to instantiate it; to avoid
70
+ # instantiation, pass :pointer as the type
71
+ def content_from( pointer, type, len=nil )
72
+ return if pointer.nil?
73
+ if type.is_a? Array
74
+ type.inject( pointer ) do |ptr, subtype|
75
+ content_for( ptr, subtype, len )
76
+ end
77
+ elsif type.is_a? Factory
78
+ type.new( pointer.read_pointer ) unless pointer.null?
79
+ elsif type.is_a?( Class ) && type.ancestors.include?( FFI::Pointer )
80
+ type.new( pointer.read_pointer ) unless pointer.null?
81
+ elsif type.is_a?( FFI::Type::Mapped )
82
+ type.from_native( pointer.read_pointer, nil ) unless pointer.null?
83
+ elsif type == :string
84
+ pointer.read_pointer.read_string(
85
+ ( len == -1 ) ? nil : len
86
+ ) unless pointer.null?
87
+ else
88
+ pointer.send( :"read_#{type}" )
89
+ end
90
+ end
91
+
92
+ # returns the the pointer's value as type
93
+ #
94
+ # for example:
95
+ # # char *get_string( void ):
96
+ # ptr = get_string()
97
+ # str = content_for( ptr, :string )
98
+ #
99
+ # # hash *get_hash( void ):
100
+ # class Hash < FFI::AutoPointer; end
101
+ # ptr = get_hash( out_ptr )
102
+ # hash = content_for( out_ptr, Hash )
103
+ def content_for( pointer, type, len=nil )
104
+ return if pointer.nil?
105
+ if type.is_a? Array
106
+ type.inject( pointer ) do |ptr, subtype|
107
+ content_for( ptr, subtype, len )
108
+ end
109
+ elsif type.is_a? Factory
110
+ type.new( pointer ) unless pointer.null?
111
+ elsif type.is_a?( Class ) && type.ancestors.include?( FFI::Pointer )
112
+ type.new( pointer ) unless pointer.null?
113
+ elsif type.is_a?( FFI::Type::Mapped )
114
+ type.from_native( pointer, nil ) unless pointer.null?
115
+ elsif type == :string
116
+ # if len is nil or -1, use it for reading instead of counting on it to
117
+ # be null-terminated
118
+ pointer.read_string( ( len == -1 ) ? nil : len ) unless pointer.null?
119
+ else
120
+ pointer.send( :"read_#{type}" ) unless pointer.null?
121
+ end
122
+ end
123
+
124
+ def pointer_for( value, type )
125
+ if type.is_a? Array
126
+ type.reverse.inject( value ) do |val, subtype|
127
+ pointer_for( val, subtype )
128
+ end
129
+ elsif type.is_a? Factory
130
+ pointer_for( value, type.real_class )
131
+ elsif type.is_a?( Class ) && type.ancestors.include?( FFI::Pointer )
132
+ # use val directly
133
+ value
134
+ elsif type.is_a?( FFI::Type::Mapped )
135
+ # mapped types are really pointers to structs; they are read
136
+ # differently, but they should still be pointers we can use directly
137
+ value
138
+ elsif type == :string
139
+ # use from_string even if it isn't necessary to null-terminate
140
+ FFI::MemoryPointer.from_string( value )
141
+ else
142
+ # it must be a FFI type, use a new MemoryPointer
143
+ ptr = FFI::MemoryPointer.new( type )
144
+ ptr.send( :"write_#{type}", value )
145
+ ptr
146
+ end
147
+ end
148
+
149
+ # this module contains extensions for classes that inherit from FFI::Struct
150
+ # and FFI::AutoPointer to make binding instance methods to C methods more
151
+ # concise
152
+ module Extensions
153
+
154
+ # convenience method that returns a Factory instance for self with the
155
+ # given args
156
+ #
157
+ # the following are equivalent:
158
+ # NativeHash.factory( :string, :string )
159
+ #
160
+ # Factory( NativeHash, :string, :string )
161
+ def factory( *args )
162
+ Factory.new( self, *args )
163
+ end
164
+
165
+ module_function
166
+
167
+ # sets the module that will be used for all bound methods, until bind_to
168
+ # is called again
169
+ def bind_to( target )
170
+ @@target = target
171
+ end
172
+
173
+ # binds a method on the current target to self
174
+ #
175
+ # == arguments
176
+ # +sym+ :: the method on the target (set by +bind_to+) to use
177
+ # +options+ :: a Hash of optional method aliases or returned arguments
178
+ #
179
+ # == options
180
+ # +:as+ :: method name to use for the bound method
181
+ # +:to+ :: function name to use from the target object
182
+ # +:returning+ :: an array of types for out parameters
183
+ # +:validate+ :: a validation to call on the bound method's return value
184
+ # +:before_return+ :: a function to call on the return value
185
+ #
186
+ # TODO: call to_proc on symbols that should be procs
187
+ def bind( sym, options={}, &block )
188
+ # look up the method in the target module
189
+ meth_name = ( options[:to] || sym ).to_sym
190
+ # get a method obj from the target receiver so that if @@target is
191
+ # changed by another call to :bind_to, the method/target will not be
192
+ # changed (and broken)
193
+ meth = @@target.method( meth_name )
194
+ name = ( options[:as] || sym ).to_sym
195
+
196
+ # get the return types as an Array and save a copy
197
+ single_return = !options[:returning].is_a?( Array )
198
+ return_types = Array( options[:returning] ).dup
199
+
200
+ # get the C function validation
201
+ validation = options[:validate]
202
+
203
+ # get the before_return filter
204
+ before_return = options[:before_return] || lambda { |x| x }
205
+
206
+ # create a new method; blocks are used to re-arrange arguments
207
+ define_method( name ) do |*args|
208
+ # create new pointers for the specified out arguments
209
+ return_ptrs = return_types.map { |type| FFI::MemoryPointer.new( type ) }
210
+
211
+ return_val = nil # keep it in scope
212
+ if block
213
+ # call the method with the arguments after re-arranging via block
214
+ return_val = meth.call( *instance_exec(
215
+ *return_ptrs, self, *args, &block
216
+ ) )
217
+ else
218
+ # call the method with the standard argument order
219
+ return_val = meth.call( *return_ptrs, self, *args )
220
+ end
221
+
222
+ # call the return check, if present
223
+ instance_exec( return_val, &validation ) if validation
224
+
225
+ # if there are return types (out pointers), read the values out of
226
+ # the pointers and replace the return_val
227
+ unless return_types.empty?
228
+ return_val = return_ptrs.zip( return_types ).map do |ptr, type|
229
+ Utils.content_from( ptr, type )
230
+ end
231
+ return_val = return_val.first if single_return
232
+ end
233
+
234
+ # run the before_return filter and return the value from it; if no
235
+ # :before_return was in options, this will be the id function
236
+ instance_exec( return_val, &before_return )
237
+ end
238
+
239
+ end
240
+
241
+ end
242
+
243
+ # extend FFI objects with the new helper methods
244
+ #FFI::Struct.extend Extensions
245
+ FFI::AutoPointer.extend Extensions
246
+
247
+ end
248
+
249
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: svn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ryan Blue
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-11 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ffi
16
+ requirement: &15276700 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *15276700
25
+ description:
26
+ email: rdblue@gmail.com
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files:
30
+ - README
31
+ files:
32
+ - lib/svn/misc.rb
33
+ - lib/svn/pools.rb
34
+ - lib/svn/apr_utils.rb
35
+ - lib/svn/diffs.rb
36
+ - lib/svn/utils.rb
37
+ - lib/svn/transactions.rb
38
+ - lib/svn/logs.rb
39
+ - lib/svn/roots.rb
40
+ - lib/svn/repos.rb
41
+ - lib/svn/streams.rb
42
+ - lib/svn/errors.rb
43
+ - lib/svn/revisions.rb
44
+ - lib/svn/counted_strings.rb
45
+ - lib/svn.rb
46
+ - README
47
+ homepage: http://github.com/codefoundry/svn
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 1.8.10
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Ruby bindings for SVN based on FFI
71
+ test_files: []