rubymurray 0.1.2

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.
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
+