rubymurray 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (9) hide show
  1. data/LICENSE +20 -0
  2. data/README +35 -0
  3. data/Rakefile +379 -0
  4. data/doc/currybook.rdoc +146 -0
  5. data/doc/jamis.rb +591 -0
  6. data/lib/curry.rb +291 -0
  7. data/setup.rb +35 -0
  8. data/test/tc_curry.rb +170 -0
  9. metadata +61 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c)2006 Ross Bamford. All rights reserved.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ 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 THE
19
+ SOFTWARE.
20
+
data/README ADDED
@@ -0,0 +1,35 @@
1
+ = Ruby Murray - Ruby version of Perl's Sub::Curry.
2
+ == (c)2006 Ross Bamford
3
+
4
+ Originally for Ruby Quiz 64. License: see +LICENSE+.
5
+ See +Curry+ for documentation and the currybook for examples.
6
+
7
+ You can download Ruby Murray from the Project Homepage on Rubyforge.
8
+
9
+ == Install
10
+
11
+ === As a Gem:
12
+
13
+ gem install rubymurray
14
+
15
+ === Manual (site-ruby)
16
+
17
+ ruby setup.rb
18
+
19
+ or
20
+
21
+ rake install
22
+
23
+ == Usage
24
+
25
+ See currybook.rdoc for examples and ideas. See also the unit-tests
26
+ included with the distribution.
27
+
28
+ == Changelog
29
+
30
+ 0.1.0 - 2005/01/27 - First version (Ruby Quiz #64)
31
+ 0.1.2 - 2005/02/02 - Changed call_spice behaviour when processing
32
+ leftover SpiceArgs, arg is now called with
33
+ no remaining args.
34
+
35
+ Released on RubyForge.
@@ -0,0 +1,379 @@
1
+ require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/gempackagetask'
4
+
5
+ SRC_RB = FileList['lib/**/*.rb']
6
+
7
+ # Determine the current version of the software
8
+ if File.read('lib/curry.rb') =~ /\s*VERSION\s*=\s*['"](\d.+)['"]/
9
+ CURRENT_VERSION = $1
10
+ else
11
+ CURRENT_VERSION = "0.0.0"
12
+ end
13
+
14
+ if ENV['REL']
15
+ PKG_VERSION = ENV['REL']
16
+ else
17
+ PKG_VERSION = CURRENT_VERSION
18
+ end
19
+
20
+ # The default task is run if rake is given no explicit arguments.
21
+
22
+ desc "Default Task (All tests)"
23
+ task :default => :alltests
24
+
25
+ # Test Tasks ---------------------------------------------------------
26
+
27
+ task :ta => :alltests
28
+ task :tu => :unittests
29
+ task :test => :unittests
30
+
31
+ Rake::TestTask.new(:alltests) do |t|
32
+ t.test_files = FileList[
33
+ 'test/tc*.rb',
34
+ 'test/contrib/tc*.rb',
35
+ 'test/fun*.rb'
36
+ ]
37
+ t.warning = true
38
+ t.verbose = true
39
+ end
40
+
41
+ Rake::TestTask.new(:unittests) do |t|
42
+ t.test_files = FileList['test/tc_*.rb']
43
+ t.warning = true
44
+ t.verbose = false
45
+ end
46
+
47
+ # Install using the standard setup.rb script.
48
+ desc "Install to systemwide site-ruby directory"
49
+ task :install do
50
+ ruby "setup.rb"
51
+ end
52
+
53
+ task :upload => :doc do
54
+ Dir.chdir('doc') do
55
+ Rake::FtpUploader.connect('/htdocs/roscopeco/code/ruby-quiz-entries/64','roscopeco.co.uk','synergix','daytona') do |ftp|
56
+ ftp.instance_eval { @ftp.passive = true }
57
+ ftp.verbose = true
58
+ ftp.upload_files('**/*')
59
+ end
60
+ end
61
+ end
62
+
63
+ # Website / Doc tasks ------------------------------------------------
64
+
65
+ desc "Build the Rdoc documentation"
66
+ rd = Rake::RDocTask.new("doc") { |rdoc|
67
+ rdoc.rdoc_dir = 'html'
68
+ rdoc.template = 'doc/jamis.rb'
69
+ rdoc.title = "Ruby Murray"
70
+ rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
71
+ rdoc.rdoc_files.include('README', 'LICENSE')
72
+ rdoc.rdoc_files.include('lib/**/*.rb', 'doc/**/*.rdoc')
73
+ rdoc.rdoc_files.exclude(/\bcontrib\b/)
74
+ }
75
+
76
+ desc "Publish the documentation and web site"
77
+ task :doc_upload => [ :doc ] do
78
+ if acct = ENV['RUBYFORGE_ACCT']
79
+ require 'rake/contrib/sshpublisher'
80
+ Rake::SshDirPublisher.new(
81
+ "#{acct}@rubyforge.org",
82
+ "/var/www/gforge-projects/rote",
83
+ "html"
84
+ ).upload
85
+ else
86
+ $stderr << "Skipping documentation upload - Need to set RUBYFORGE_ACCT to your rubyforge.org user name"
87
+ end
88
+ end
89
+
90
+ # ====================================================================
91
+ # Create a task that will package the software into distributable
92
+ # tar, zip and gem files.
93
+
94
+ PKG_FILES = FileList[
95
+ 'setup.rb',
96
+ '[A-Z]*',
97
+ 'lib/**/*.rb',
98
+ 'test/**/*',
99
+ 'doc/**/*'
100
+ ]
101
+
102
+ if ! defined?(Gem)
103
+ puts "Package Target requires RubyGEMs"
104
+ else
105
+ spec = Gem::Specification.new do |s|
106
+
107
+ #### Basic information.
108
+
109
+ s.name = 'rubymurray'
110
+ s.version = PKG_VERSION
111
+ s.summary = "A Ruby port of Perl's Sub::Curry"
112
+ s.description = <<-EOF
113
+ Ruby Murray is a Ruby port of Perl's Sub::Curry,
114
+ originally written for Ruby Quiz #64 (Port a library).
115
+ EOF
116
+
117
+ #### Which files are to be included in this gem?
118
+
119
+ s.files = PKG_FILES.to_a
120
+
121
+ #### Load-time details
122
+ s.require_path = 'lib' # Use these for libraries.
123
+
124
+ #### Documentation and testing.
125
+ s.has_rdoc = true
126
+ s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
127
+ s.rdoc_options <<
128
+ '--title' << 'Ruby Murray' <<
129
+ '--main' << 'README' <<
130
+ '--line-numbers' <<
131
+ '--inline-source' <<
132
+ '--template' << 'doc/jamis.rb'
133
+ '-o' << 'html'
134
+
135
+ s.test_files = Dir.glob('test/gem_*.rb')
136
+
137
+ #### Author and project details.
138
+
139
+ s.author = "Ross Bamford"
140
+ s.email = "ross@roscopeco.co.uk"
141
+ s.homepage = "http://rubymurray.rubyforge.org"
142
+ s.rubyforge_project = "rubymurray"
143
+ end
144
+
145
+ # Quick fix for Ruby 1.8.3 / YAML bug
146
+ if (RUBY_VERSION == '1.8.3')
147
+ def spec.to_yaml
148
+ out = super
149
+ out = '--- ' + out unless out =~ /^---/
150
+ out
151
+ end
152
+ end
153
+
154
+ package_task = Rake::GemPackageTask.new(spec) do |pkg|
155
+ pkg.need_zip = true
156
+ pkg.need_tar_gz = true
157
+ pkg.package_dir = 'pkg'
158
+ end
159
+ end
160
+
161
+ # Misc tasks =========================================================
162
+
163
+ def count_lines(filename)
164
+ lines = 0
165
+ codelines = 0
166
+ open(filename) { |f|
167
+ f.each do |line|
168
+ lines += 1
169
+ next if line =~ /^\s*$/
170
+ next if line =~ /^\s*#/
171
+ codelines += 1
172
+ end
173
+ }
174
+ [lines, codelines]
175
+ end
176
+
177
+ def show_line(msg, lines, loc)
178
+ printf "%6s %6s %s\n", lines.to_s, loc.to_s, msg
179
+ end
180
+
181
+ desc "Count total lines in source"
182
+ task :lines do
183
+ total_lines = 0
184
+ total_code = 0
185
+ show_line("File Name", "LINES", "LOC")
186
+ SRC_RB.each do |fn|
187
+ lines, codelines = count_lines(fn)
188
+ show_line(fn, lines, codelines)
189
+ total_lines += lines
190
+ total_code += codelines
191
+ end
192
+ show_line("TOTAL", total_lines, total_code)
193
+ end
194
+
195
+ ARCHIVEDIR = '/mnt/usb'
196
+
197
+ task :archive => [:package] do
198
+ cp FileList["pkg/*.tar.gz", "pkg/*.zip", "pkg/*.gem"], ARCHIVEDIR
199
+ end
200
+
201
+ # Support Tasks ------------------------------------------------------
202
+
203
+ desc "Look for TODO and FIXME tags in the code"
204
+ task :todo do
205
+ FileList['**/*.rb'].egrep /#.*(FIXME|TODO|TBD)/
206
+ end
207
+
208
+ desc "Look for Debugging print lines"
209
+ task :dbg do
210
+ FileList['**/*.rb'].egrep /\bDBG|\bbreakpoint\b/
211
+ end
212
+
213
+ desc "List all ruby files"
214
+ task :rubyfiles do
215
+ puts Dir['**/*.rb'].reject { |fn| fn =~ /^pkg/ }
216
+ puts Dir['bin/*'].reject { |fn| fn =~ /CVS|(~$)|(\.rb$)/ }
217
+ end
218
+
219
+ desc "Show deprecation notes"
220
+ task :deprecated do
221
+ Dir['lib/**/*.r?'].each do |fn|
222
+ File.open(fn) do |f|
223
+ [*f].each_with_index do |line,i|
224
+ if line =~ /#(.*)vv([0-9\.]+)(?:[\s\t]*)v-([0-9\.]+)/
225
+ cmnt = $~[1].strip
226
+ cmnt = '<No comment>' if (cmnt.nil? or cmnt.empty?)
227
+ printf "%s:%d\n%-60s %5s %5s\n\n", fn, i+1, cmnt, $~[2].strip, $~[3].strip
228
+ end #if splits okay
229
+ end #each_w_idx
230
+ end #fopen
231
+ end #dir
232
+ end
233
+
234
+ desc "Find features deprecated at VER"
235
+ task :deprecated_by do
236
+ fail "\nYou must specify the version to check (VER=x.y.z)\n\n" unless ver = ENV['VER']
237
+ Dir['lib/**/*.r?'].each do |fn|
238
+ File.open(fn) do |f|
239
+ [*f].each_with_index do |line,i|
240
+ if line =~ /vv#{ver}/
241
+ if line =~ /#(.*)vv([0-9\.]+)(?:[\s\t]*)v-([0-9\.]+)/
242
+ cmnt = $~[1].strip
243
+ cmnt = '<No comment>' if (cmnt.nil? or cmnt.empty?)
244
+ printf "%s:%d\n%-60s %5s %5s\n\n", fn, i+1, cmnt, $~[2].strip, $~[3].strip
245
+ end #if splits okay
246
+ end #if line is dep remove
247
+ end #each_w_idx
248
+ end #fopen
249
+ end #dir
250
+ end
251
+
252
+ desc "Find deprecated features to be removed by VER"
253
+ task :deprecated_due do
254
+ fail "\nYou must specify the version to check (VER=x.y.z)\n\n" unless ver = ENV['VER']
255
+ Dir['lib/**/*.r?'].each do |fn|
256
+ File.open(fn) do |f|
257
+ [*f].each_with_index do |line,i|
258
+ if line =~ /v-#{ver}/
259
+ if line =~ /#(.*)vv([0-9\.]+)(?:[\s\t]*)v-([0-9\.]+)/
260
+ cmnt = $~[1].strip
261
+ cmnt = '<No comment>' if (cmnt.nil? or cmnt.empty?)
262
+ printf "%s:%d\n%-60s %5s %5s\n\n", fn, i+1, cmnt, $~[2].strip, $~[3].strip
263
+ end #if splits okay
264
+ end #if line is dep remove
265
+ end #each_w_idx
266
+ end #fopen
267
+ end #dir
268
+ end
269
+
270
+ # --------------------------------------------------------------------
271
+ # Creating a release
272
+
273
+ def announce(msg='')
274
+ STDERR.puts msg
275
+ end
276
+
277
+ desc "Make a new release"
278
+ task :release => [
279
+ :prerelease,
280
+ :clobber,
281
+ :alltests,
282
+ :update_version,
283
+ :package,
284
+ :tag,
285
+ :doc_upload] do
286
+
287
+ announce
288
+ announce "**************************************************************"
289
+ announce "* Release #{PKG_VERSION} Complete."
290
+ announce "* Packages ready to upload."
291
+ announce "**************************************************************"
292
+ announce
293
+ end
294
+
295
+ # Validate that everything is ready to go for a release.
296
+ task :prerelease do
297
+ announce
298
+ announce "**************************************************************"
299
+ announce "* Making RubyGem Release #{PKG_VERSION}"
300
+ announce "* (current version #{CURRENT_VERSION})"
301
+ announce "**************************************************************"
302
+ announce
303
+
304
+ # Is a release number supplied?
305
+ unless ENV['REL']
306
+ fail "Usage: rake release REL=x.y.z [REUSE=tag_suffix]"
307
+ end
308
+
309
+ # Is the release different than the current release.
310
+ # (or is REUSE set?)
311
+ if PKG_VERSION == CURRENT_VERSION && ! ENV['REUSE']
312
+ fail "Current version is #{PKG_VERSION}, must specify REUSE=tag_suffix to reuse version"
313
+ end
314
+
315
+ # Are all source files checked in?
316
+ if ENV['RELTEST']
317
+ announce "Release Task Testing, skipping checked-in file test"
318
+ else
319
+ announce "Checking for unchecked-in files..."
320
+ data = `svn status`
321
+ unless data =~ /^$/
322
+ fail "SVN status is not clean ... do you have unchecked-in files?"
323
+ end
324
+ announce "No outstanding checkins found ... OK"
325
+ end
326
+ end
327
+
328
+ task :update_version => [:prerelease] do
329
+ if PKG_VERSION == CURRENT_VERSION
330
+ announce "No version change ... skipping version update"
331
+ else
332
+ announce "Updating Curry version to #{PKG_VERSION}"
333
+ open("lib/curry.rb") do |rakein|
334
+ open("lib/curry.rb.new", "w") do |rakeout|
335
+ rakein.each do |line|
336
+ if line =~ /^\s*VERSION\s*=\s*/
337
+ rakeout.puts " VERSION = '#{PKG_VERSION}'"
338
+ else
339
+ rakeout.puts line
340
+ end
341
+ end
342
+ end
343
+ end
344
+ mv "lib/curry.rb.new", "lib/curry.rb"
345
+ if ENV['RELTEST']
346
+ announce "Release Task Testing, skipping commiting of new version"
347
+ else
348
+ sh %{svn commit -m "Updated to version #{PKG_VERSION}" lib/curry.rb}
349
+ end
350
+ end
351
+ end
352
+
353
+ desc "Create a new SVN tag with the latest release number (REL=x.y.z)"
354
+ task :tag => [:prerelease] do
355
+ reltag = "REL_#{PKG_VERSION.gsub(/\./, '_')}"
356
+ reltag << ENV['REUSE'].gsub(/\./, '_') if ENV['REUSE']
357
+ announce "Tagging CVS with [#{reltag}]"
358
+ if ENV['RELTEST']
359
+ announce "Release Task Testing, skipping SVN tagging"
360
+ else
361
+ # need to get current base URL
362
+ s = `svn info`
363
+ if s =~ /URL:\s*([^\n]*)\n/
364
+ svnroot = $1
365
+ if svnroot =~ /^(.*)\/trunk/
366
+ svnbase = $1
367
+ sh %{svn cp #{svnroot} #{svnbase}/tags/#{reltag} -m "Release #{PKG_VERSION}"}
368
+ else
369
+ fail "Please merge to trunk before making a release"
370
+ end
371
+ else
372
+ fail "Unable to determine repository URL from 'svn info' - is this a working copy?"
373
+ end
374
+ end
375
+ end
376
+
377
+ # Require experimental XForge/Metaproject support.
378
+ # load 'xforge.rf' if File.exist?('xforge.rf')
379
+
@@ -0,0 +1,146 @@
1
+ == The Ruby Murray Cookbook - Recipes for Ruby's Sub::Curry port.
2
+
3
+ This is a translation of the Sub::Curry cookbook to Ruby Murray.
4
+ See the original at:
5
+
6
+ * http://search.cpan.org/~lodin/Sub-Curry-0.8/lib/Sub/Curry/Cookbook.pod
7
+
8
+ (c)2006 Ross Bamford. Same license as Ruby.
9
+
10
+ In case you're wondering, see http://en.wikipedia.org/wiki/Ruby_Murray.
11
+
12
+ === Right currying
13
+
14
+ Special spices make rcurry unnecessary. You can add trailing
15
+ spice using blackholes.
16
+
17
+ c = lambda { |a,b,c| (a + b) / c }.curry(Curry::BLACKHOLE, 2)
18
+ c.call(5,3)
19
+ # => 4
20
+
21
+ === Convert func -> method / method -> func / Curry a method
22
+
23
+ These are not really applicable to Ruby - the original was working around
24
+ Perl's broken implementation. But I guess something like:
25
+
26
+ def send_func(obj, msg, *args)
27
+ obj.send(msg,*args)
28
+ end
29
+
30
+ c = method(:send_func).curry("object")
31
+
32
+ c.call(:length)
33
+ # => 6
34
+
35
+ c.call(:+, "ive C")
36
+ # => "objective C"
37
+
38
+ And (more useful?):
39
+
40
+ c = "string".method(:slice).curry(Curry::HOLE,2)
41
+ c.call(0)
42
+ # => "st"
43
+ c.call(2)
44
+ # => "ri"
45
+
46
+ === Add trailing spice after a hole in a curried proc
47
+
48
+ You have a hole in your curried subroutine but want to add more spices
49
+ after or fill out holes after your first hole
50
+
51
+ Assuming no special spice before the hole, just put a hole in the hole.
52
+
53
+ plus = method(:send_func).curry(Curry::HOLE,:+)
54
+ "#{plus.spice.inspect} -> #{plus.call("object","ive")}"
55
+ # => "[<HOLE>, :+] -> objective"
56
+
57
+ plusion = plus.new(Curry::HOLE, "ion")
58
+ "#{plusion.spice.inspect} -> #{plusion.call("object")}"
59
+ # => "[<HOLE>, :+, "ion"] -> objection"
60
+
61
+ === Add trailing spice after a blackhole
62
+
63
+ You want to add more spice, but have a blackhole that swallows anything
64
+ you try to put in there. You want to keep the blackhole, though.
65
+
66
+ withtwo = method(:send_func).curry(Curry::BLACKHOLE,2)
67
+ "#{withtwo.spice.inspect} -> #{withtwo.call([1,2,3,4],:index).inspect}"
68
+ # => "[<BLACKHOLE>, 2] -> 1"
69
+
70
+ Using a blackhole follwed by a whitehole, we can add extra spice at the
71
+ end:
72
+
73
+ twooh = withtwo.new(Curry::BLACKHOLE, Curry::WHITEHOLE, 0)
74
+ "#{twooh.spice.inspect} -> #{twooh.call([1,2,3,4],:[]=).inspect}"
75
+ # => "[<BLACKHOLE>, 2, 0] -> 0"
76
+
77
+ Or by placing spice between the black and white holes we can place it
78
+ immediately after the original blackhole.
79
+
80
+ ohtwo = withtwo.new(Curry::BLACKHOLE, 0, Curry::WHITEHOLE)
81
+ "#{ohtwo.spice.inspect} -> #{ohtwo.call([1,2,3,4],:slice).inspect}"
82
+ # => "[<BLACKHOLE>, 0, 2] -> [1, 2]"
83
+
84
+ By giving the blackhole another blackhole, you have two successive
85
+ blackholes, the first newly added and the second the one that added the
86
+ new. The whitehole is then removes the second blackhole -- the blackhole
87
+ doing the inserting. The spice is then added like usual, yet a blackhole
88
+ exists at the right place.
89
+
90
+ === Append raw spice without processing
91
+
92
+ You have a curry that may have special spices in it. You want to add more
93
+ spices. You do not want the new spice to be processed as an application on
94
+ the old spice. You just want to append more spice.
95
+
96
+ To do this, you'll have to manually get the parts you need and make
97
+ a new curry, since applying the new spice the usual way results in it
98
+ being processed.
99
+
100
+ sary = lambda { |*args| args }.curry(Curry::HOLE,'Array')
101
+ "#{sary.spice.inspect} -> #{sary.call('The',2,3).inspect}"
102
+ # => "[<HOLE>, "Array"] -> ["The", "Array", 2, 3]"
103
+
104
+ sary2 = Curry.new(*sary.spice + [Curry::BLACKHOLE, 'The end'], &sary.uncurried)
105
+ "#{sary2.spice.inspect} -> #{sary2.call('The',2,3).inspect}"
106
+ # => "[<HOLE>, "Array", <BLACKHOLE>, "The end"] -> ["The", "Array", 2, 3, "The end"]"
107
+
108
+ === Pass curries as blocks to methods
109
+
110
+ Curry implements the to_proc protocol so you're away.
111
+
112
+ adjsum = lambda { |sum, val, adj| sum + (val * adj) }.curry(Curry::BLACKHOLE, 2)
113
+ [1,2,3,4,5].inject(0,&adjsum)
114
+ # => 30
115
+
116
+ === Apply block arguments via spice
117
+
118
+ Right now, Curry doesn't treat block arguments in the spice specially, and in
119
+ particular won't unwrap (with &) a block at the end of your spice. Also, since
120
+ it's not possible to pass a block to another block, you cannot pass blocks
121
+ to your curried procs or chain them together.
122
+
123
+ You *can* pass blocks to curried methods in a call, but *only* when they're
124
+ created from bound methods (passed before the spice, or from Method#curry).
125
+
126
+ curry = [10,20,30].method(:inject).curry(Curry::HOLE)
127
+ curry.call(0) { |s,i| s + i }
128
+ # => 60
129
+
130
+ Taking this a step further, you can do some pretty mad stuff:
131
+
132
+ mult_sum = lambda { |sum, i, mult| sum + (i * mult) }.curry(Curry::BLACKHOLE, Curry::HOLE)
133
+
134
+ double_sum = mult_sum.new(Curry::BLACKHOLE, Curry::WHITEHOLE, 2)
135
+ triple_sum = mult_sum.new(Curry::BLACKHOLE, Curry::WHITEHOLE, 3)
136
+
137
+ curry.call(0, &double_sum)
138
+ # => 120
139
+
140
+ curry.call(0, &triple_sum)
141
+ # => 180
142
+
143
+ (Wow, that's pretty useless ;)) Ruby doesn't allow block arguments to blocks,
144
+ so a block attached to a curried proc invocation is ignored.
145
+
146
+