From: rubikitch@... Date: 2005-10-11T09:48:15+09:00 Subject: [ANN] el4r-0.9.3 - EmacsLisp for Ruby Hi, El4r enables you to write Emacs programs in Ruby as well as in EmacsLisp. I call the Ruby language to manipulate Emacs `EmacsRuby'. El4r and Test::Unit enables you to test EmacsLisp/EmacsRuby programs automatically. El4r is available at http://www.rubyist.net/~rubikitch/computer/el4r/index.en.html == What's new === [2005/10/11] 0.9.3 released * el4r-runtest.rb: Raise when the test-script file is not found. * Now output builtin functions (p, print, puts ...) are usable in EmacsRuby. * Fixed an error-handling bug. * More stable. === [2005/10/05] 0.9.2 released * ~/.el4rrc.rb contains all the el4r setting. * Automatic configuration. * Now el4r can be installed in arbitrary directory. * A block is accepted in El4r::ELMethodsMixin#newbuf . * New class: ElApp * Works with Windows(WINE). * Introduced el4r_load search path. * New EmacsRuby library in el4r/ directory. == Download / Install / Setup El4r currently be installed in your home directory. If you got error when downloading, you must update Ruby. Here is the shell commands to download, install and setup. el4r-rctool setups and updates your dotfiles automatically. To update older el4r (<= 0.9.1), you must remove these lines from ~/.emacs, (add-to-list 'load-path "~/src/el4r/elisp/") (require 'el4r) (el4r-boot) and this line from ~/.el4r/init.rb by hand. el4r_load "el4r-mode.rb" In newer el4r, el4r-rctool updates your dotfiles automatically. ruby -ropen-uri -e 'URI("http://www.rubyist.net/~rubikitch/archive/el4r-0.9.3.tar.gz").read.display' | tar xzf - cd el4r-0.9.3 ruby el4r-rctool -p ruby el4r-rctool -i The diretory to put EmacsRuby scripts is ~/.el4r by default. The environment variable EL4R_HOME sets the directory to put EmacsRuby scripts. Here is a test program of el4r. # el4r - EmacsLisp for Ruby # Copyright (C) 2005 rubikitch # Version: $Id: test-el4r.rb,v 1.37 2005/10/10 23:58:15 rubikitch Exp $ # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA require 'test/unit' require 'tempfile' require 'tmpdir' require 'pathname' require 'fileutils' class << Tempfile def path(content, dir=Dir.tmpdir) x = Tempfile.open("content", dir) x.write content x.close x.open x.path end def pathname(content, dir=Dir.tmpdir) Pathname.new(path(content, dir=Dir.tmpdir)) end end # El4r self test. class TestEl4r < Test::Unit::TestCase # ElMixin is already included/extended. # So we can write EmacsRuby in this class. # Testing ELListCell#to_ary. # This method enables us to multiple assignment. def test_to_ary list = el4r_lisp_eval(%q((list 1 2))) one, two = list assert_equal(list.to_a, list.to_ary) assert_equal(1, one) assert_equal(2, two) end # Testing with and match-string. def test_match_string lisp = %q((progn (switch-to-buffer "a") (save-excursion (insert "abcdefg\n") (goto-char 1) (re-search-forward "^\\\\(.+\\\\)$") ) (match-string 1))) ruby = lambda{ ##### [with] with(:save_excursion) do goto_char 1 re_search_forward('^\\(.+\\)$') end match_string 1 ##### [/with] } assert_equal(el4r_lisp_eval(lisp), ruby[]) end # helper method: # execute a block with temporary buffer. # and return the contents of buffer. def with_temp_buffer_string(&block) with(:with_temp_buffer){ self.instance_eval(&block) buffer_string } end # this test was in test.el def test_test_el__debug_ruby_eval_report actual = with_temp_buffer_string { el4r_lisp_eval %q((progn (el4r-debug-ruby-eval-report "nil") (el4r-debug-ruby-eval-report "true") (el4r-debug-ruby-eval-report "false") (el4r-debug-ruby-eval-report "1 + 6") (el4r-debug-ruby-eval-report "\"String\"") )) } expected = < nil true => t false => nil 1 + 6 => 7 "String" => "String" EOB assert_equal(expected, actual) end def test_test_el__condition_case # (mode-info-describe-function 'signal 'elisp) # (mode-info-describe-function 'condition-case 'elisp) el4r_lisp_eval %q((progn (put 'test-error 'error-conditions '(error test-error)) (put 'test-error 'error-message "Test Error") )) #' el4r_lisp_eval %q((progn (setq error-desc nil) (condition-case err (signal 'test-error '(123)) (test-error (setq error-desc (format "Error is passed: %s" err))) ) )) #' assert_equal("Error is passed: (test-error 123)", elvar.error_desc) el4r_lisp_eval %q((progn (setq error-desc nil) (condition-case err (el4r-ruby-eval "el4r_lisp_eval(\"(signal 'test-error '(123))\")") (test-error (setq error-desc (format "Error is passed: %s" err))) ) )) #' assert_equal("Error is passed: (test-error 123)", elvar.error_desc) end # eval test def test_el4r_eval result = with_temp_buffer_string{ el4r_lisp_eval(<<'EOF') (insert-string (el4r-ruby-eval "\"Hello from ruby from emacs from ruby!\n\"")) EOF } assert_equal("Hello from ruby from emacs from ruby!\n", result) assert_equal(true, el4r_lisp_eval('t')) end # list: cons, car/cdr def test_list list = el("'(3 2 1)") list = cons(4, list) assert_equal("(4 3 2 1)", prin1_to_string(list)) ary = [] while list ary << car(list) list = cdr(list) end assert_equal("[4, 3, 2, 1]", ary.inspect) end # pass a Ruby object to Emacs def test_object obj = Object.new assert_equal("Is ruby object passed? ... true", "Is ruby object passed? ... #{car(cons(obj, nil)) == obj}") end # Using defun ( Proc -> Lambda conversion ) def test_defun_function defun(:my_ruby_func) { |a| "String from my_ruby_func: '#{a}'" } assert_equal("String from my_ruby_func: 'Hello!'", my_ruby_func("Hello!")) end # defun a command def test_defun_command_1 defun(:my_command, :interactive => true) { insert_string("My Interactive command from Ruby."); newline } assert_equal("My Interactive command from Ruby.\n", with_temp_buffer_string{ call_interactively(:my_command) }) end # defun a command with docstring def test_defun_command_2 ##### [my_command2] defun(:my_command2, :interactive => "d", :docstring => "description...") { |point| insert_string("Current point is #{point}."); newline } ##### [/my_command2] assert_equal("d", nth(1, commandp(:my_command2))) assert_equal("description...", documentation(:my_command2)) assert_equal("Current point is 1.\n", with_temp_buffer_string{ call_interactively(:my_command2) }) end # defining odd-named function def test_defun_oddname # Lisp can define `1+1' function! LOL defun("1+1"){2} assert_equal(2, funcall("1+1")) end # Calling lambda def test_lambda lambda = el4r_lisp_eval("(lambda (i) (+ i 1))") assert_equal(2, funcall(lambda, 1)) end # Calling special form like save-excursion def test_with x = with_temp_buffer_string { insert_string("a\n") with(:save_excursion) { beginning_of_buffer insert_string("This is inserted at the beginning of buffer."); newline } } assert_equal("This is inserted at the beginning of buffer.\na\n", x) end # ELListCell def test_ELListCell assert_equal([1, 2], cons(1, cons(2, nil)).to_a ) assert_equal([10,20], el4r_lisp_eval(%((list 1 2))).map{|x| x*10}) assert_equal({'a'=>1, 'b'=>2}, list(cons("a",1), cons("b", 2)).to_hash) assert_raises(TypeError){ list(cons("a",1), "b", "c").to_hash } end # ELVector def test_ELVector v = el4r_lisp_eval("[1 2]") assert( vectorp(v) ) assert_equal("ELVector[1, 2]", v.inspect) assert_equal(1, v[0]) assert_equal(1, v[-2]) assert_raises(ArgumentError) { v[2] } # index is too large assert_raises(TypeError) { v["X"] } assert_equal([1, 2], v[0,2]) assert_equal([1, 2], v.to_a) # Enumerable assert_equal(1, v.find{|x| x==1}) # to_ary one, = v assert_equal(1, one) # aset elvar.v = v assert_equal(10, v[0]=10) assert_equal(10, v[0]) assert_equal([10,2], v.to_a) assert_equal(10, elvar.v[0]) assert_equal([10,2], elvar.v.to_a) assert_raises(ArgumentError) { v[2]=3 } # index is too large assert_raises(TypeError) { v["X"]=1 } v[-1]=20 assert_equal([10,20], elvar.v.to_a) end # Accessing to lisp variables with elvar def test_elvar elvar.myvar = 123 assert_equal(123, elvar.myvar) elvar["myvar"] = 456 assert_equal(456, elvar["myvar"]) assert( elvar.myvar == elvar["myvar"] ) end # get/set an odd-named variable def test_elvar__oddname elvar["*an/odd+variable!*"] = 10 assert_equal(10, elvar["*an/odd+variable!*"]) end # Error passing def test_error assert_raises(RuntimeError) { el4r_lisp_eval(<<-'EOF') (el4r-ruby-eval "raise \"Is error handled correctly?\"")) EOF } end # let def test_let elvar.testval = 12 testval_in_letblock = nil let(:testval, 24) { testval_in_letblock = elvar.testval } assert_equal(24, testval_in_letblock) assert_equal(12, elvar.testval) end # Regexp convert: Convert Ruby regexps to MESSY Emacs regexps. def test_regexp # (find-node "(emacs-ja)Regexps") conv = lambda{|from,to| assert_equal(to, el4r_conv_regexp(from)) } conv[ //, '' ] conv[ /a/, 'a' ] conv[ /a./, 'a.' ] conv[ /a*/, 'a*' ] conv[ /a+/, 'a+' ] conv[ /a?/, 'a?' ] conv[ /[ab]/, '[ab]' ] conv[ /[^ab]/, '[^ab]' ] conv[ /^ab/, '^ab' ] conv[ /ab$/, 'ab$' ] conv[ /a|b/, 'a\|b' ] conv[ /(ab)/, '\(ab\)' ] conv[ /\As/, '\`s' ] conv[ /s\Z/, %q[s\'] ] # \= conv[ /\bball\B/, '\bball\B'] # \< # \> conv[ /\w/, '[0-9A-Za-z_]'] conv[ /\W/, '[^0-9A-Za-z_]'] # \sC # \SC # \D (number) end # Now you can specify a Ruby regexp to string-match, re-search-forward and so on def test_string_match s = "a" assert_equal(0, string_match("a", s)) assert_equal(0, string_match(/a/, s)) assert_equal(0, string_match('\(a\|\b\)', s)) assert_equal(0, string_match(/a|b/, s)) assert_equal(0, string_match(/^a/, s)) assert_equal(0, string_match(/a$/, s)) assert_equal(0, string_match(/.*/, s)) assert_equal(nil, string_match(/not-match/, s)) end # ElMixin: elisp {} def test_elmixin eval %{ class ::Foo include ElMixin def foo elisp { [self.class, outer.class] } end def one 1 end end } el4r, outer = Foo.new.foo assert_equal(El4r::ELInstance, el4r) assert_equal(Foo, outer) end # EL error def test_elerror errormsg = nil begin el4r_lisp_eval(%q((defun errorfunc0 ()))) with(:with_current_buffer, "*scratch*"){ let(:x, 1) { with(:save_excursion){ errorfunc0 1 # wrong number of argument!! } } } flunk rescue errormsg = $!.to_s end assert_match(/\n\(errorfunc0.+save-excursion.+let.+with-current-buffer.+$/m, errormsg.to_s) end # to_s: Implicitly call prin1_to_string def test_to_s list = list(1) assert_equal("(1)", "#{list}") assert_equal( prin1_to_string(list), list.to_s) end # defadvice 1 def test_defadvice_1 defun(:adtest1){ elvar.v = 1 } with(:defadvice, el("adtest1 (after adv activate)")){ elvar.v = 2 } adtest1 assert_equal(2, elvar.v) end # defadvice 2 def test_defadvice_2 elvar.w = 0 elvar.x = 0 defun(:adtest2){ elvar.w += 1 3 } defadvice(:adtest2, :around, :adv2, :activate) { ad_do_it elvar.x = 10 ad_do_it } ret = adtest2() assert_equal(2, elvar.w) assert_equal(10, elvar.x) assert_equal(3, ret) end # defadvice 3 def test_defadvice_3 begin ##### [adtest3] # define a function defun(:adtest3){ 1 } ##### [/adtest3] assert_equal(1, adtest3()) assert_equal(nil, commandp(:adtest3)) ##### [adtest3-advice] # now define an advice defadvice(:adtest3, :around, :adv3, :activate, :docstring=>"test advice", :interactive=>true) { ad_do_it elvar.ad_return_value = 2 } ##### [/ad_return_value] assert(commandp(:adtest3)) assert_equal(2, adtest3()) assert_match(/test advice/, documentation(:adtest3)) ensure ad_deactivate :adtest3 # fmakunbound :adtest3 end end # bufstr def test_bufstr s = bufstr(newbuf(:name=>"axx", :contents=>"foo!")) assert_equal("foo!", s) newbuf(:name=>"axxg", :contents=>"bar!", :current=>true) s = bufstr assert_equal("bar!", s) end def xtest_ad_do_it_invalid assert_raises(El4r::El4rError){ ad_do_it } end # el_load def test_el_load begin el = File.expand_path("elloadtest.el") open(el, "w"){|w| w.puts(%q((setq elloadtest 100)))} el_load(el) assert_equal(100, elvar.elloadtest) ensure File.unlink el end end # equality def test_EQUAL b1 = current_buffer b2 = current_buffer assert(b1 == b1) assert(b1 == b2) assert_equal(b1,b2) end # test delete-other-windows workaround in xemacs def test_delete_other_windows w = selected_window elvar.window_min_height = 1 split_window split_window delete_other_windows assert(one_window_p) assert(eq(w, selected_window)) end # Lisp string -> Ruby string special case def test_el4r_lisp2ruby__normal cmp = lambda{|str| assert_equal(str, eval(el4r_lisp2ruby(str)))} # (mode-info-describe-function 'prin1-to-string 'elisp) # (string= "\021" (el4r-ruby-eval (el4r-lisp2ruby "\021"))) cmp[ "" ] cmp[ "a"*999999 ] cmp[ '1' ] cmp[ 'a' ] cmp[ '\\' ] cmp[ '\\\\' ] cmp[ '\\\\\\' ] cmp[ '""' ] cmp[ '"' ] cmp[ "''" ] cmp[ '#{1}' ] cmp[ '\#{1}' ] cmp[ '#{\'1\'}' ] cmp[ '#@a' ] cmp[ "\306\374\313\334\270\354" ] # NIHONGO in EUC-JP end def test_el4r_lisp2ruby__treat_ctrl_codes cmp = lambda{|str| assert_equal(str, eval(el4r_lisp2ruby(str)))} el4r_treat_ctrl_codes { cmp[ "" ] cmp[ "a"*999999 ] cmp[ '1' ] cmp[ 'a' ] cmp[ '\\' ] cmp[ '\\\\' ] cmp[ '\\\\\\' ] cmp[ '""' ] cmp[ '"' ] cmp[ "''" ] cmp[ '#{1}' ] cmp[ '\#{1}' ] cmp[ '#{\'1\'}' ] cmp[ '#@a' ] cmp[ "\306\374\313\334\270\354" ] # NIHONGO in EUC-JP cmp[ "\ca" ] cmp[ "\cb" ] cmp[ "\cc" ] cmp[ "\cd" ] cmp[ "\ce" ] cmp[ "\cf" ] cmp[ "\cg" ] cmp[ "\ch" ] cmp[ "\ci" ] cmp[ "\cj" ] cmp[ "\ck" ] cmp[ "\cl" ] # C-m # cmp[ "\cn" ] failed on xemacs # cmp[ "\co" ] failed on xemacs cmp[ "\cp" ] cmp[ "\cq" ] # C-r cmp[ "\cs" ] cmp[ "\ct" ] cmp[ "\cu" ] cmp[ "\cv" ] cmp[ "\cw" ] cmp[ "\cx" ] # cmp[ "\cy" ] cmp[ "\cz" ] } end def el4r_load_test_helper(dir) begin $loaded = nil tmpscript = "#{dir}/__testtmp__.rb" "$loaded = true".writef(tmpscript) el4r_load "__testtmp__.rb" assert_equal(true, $loaded) ensure FileUtils.rm_f tmpscript end end def test_el4r_load__load_path load_path = el4r.conf.el4r_load_path load_path.each do |dir| FileUtils.mkdir_p dir el4r_load_test_helper dir end end def test_el4r_load__not_exist assert_raises(LoadError) { el4r_load "__not_exist.rb" } assert_equal(false, el4r_load("__not_exist.rb", true)) end def test_el4r_load__order begin $loaded = nil load_path = el4r.conf.el4r_load_path = [ el4r_homedir, el4r.site_dir ] FileUtils.mkdir_p load_path rb = "__testtmp__.rb" home_rb = File.expand_path(rb, el4r_homedir) site_rb = File.expand_path(rb, el4r.site_dir) "$loaded = :OK".writef(home_rb) "$loaded = :NG".writef(site_rb) el4r_load rb assert_equal(:OK, $loaded) ensure FileUtils.rm_f [home_rb, site_rb] end end def test_stdlib_loaded assert_equal(true, fboundp(:winconf_push)) end def test_winconf # make a winconf switch_to_buffer "a buffer" insert "string" pt = point # current_window_configuration does not works with xemacs -batch. I do not know why. assert( one_window_p ) buf = current_buffer winconf_push # alter the winconf goto_char 1 split_window winconf_pop # revive the winconf assert( one_window_p ) assert_equal(buf, current_buffer) assert_equal(pt, point) end def test_el4r_output printf("\t\n\ca!%s!","a") print(1) assert_equal("\t\n\ca!a!1", bufstr("*el4r:output*")) end # end of TestEl4r end # newbuf examples class TestNewbuf < Test::Unit::TestCase include ElMixin def setup @bufname = "buffer-does-not-exist!!!" end def teardown kill_buffer(@bufname) if get_buffer(@bufname) end def setbuf set_buffer @x end def test_create @x = newbuf(:name=>@bufname) setbuf assert_equal(true, bufferp(@x)) assert_equal(@bufname, buffer_name(@x)) assert_equal("", buffer_string) y = newbuf(:name=>@bufname) assert(eq(@x,y)) end def test_contents @x = newbuf(:name=>@bufname, :contents=>"foo") setbuf assert_equal("foo", buffer_string) assert_equal(4, "foo".length+1) assert_equal(4, point) # buffer is erased @x = newbuf(:name=>@bufname, :contents=>"bar") setbuf assert_equal("bar", buffer_string) end def test_file begin file = Tempfile.path("abcd") @x = newbuf(:file=>file) setbuf assert_equal(file, buffer_file_name) assert_equal("abcd", buffer_string) ensure kill_buffer nil File.unlink file end end def test_name_and_file begin file1 = Tempfile.path("abcd") @x = newbuf(:name=>@bufname, :file=>file1) setbuf assert_equal(nil, buffer_file_name) assert_equal("abcd", buffer_string) # buffer is erased file2 = Tempfile.path("abcde") @x = newbuf(:name=>@bufname, :file=>file2) setbuf assert_equal("abcde", buffer_string) ensure kill_buffer nil File.unlink file1 File.unlink file2 end end def test_argerror assert_raises(ArgumentError){ newbuf } assert_raises(ArgumentError){ newbuf(:name=>nil) } assert_raises(ArgumentError){ newbuf(1) } assert_raises(ArgumentError){ newbuf("1") } # hmm. assert_raises(ArgumentError){ newbuf(:name=>@bufname, :line=>"a") } assert_raises(ArgumentError){ newbuf(:name=>@bufname, :point=>"a") } end def test_current_line @x = newbuf(:name=>@bufname, :contents=>"a\nb\nc\nd", :line=>2) setbuf assert_equal("b", char_to_string(char_after)) end def test_point @x = newbuf(:name=>@bufname, :contents=>"abcde", :point=>2) setbuf assert_equal("b", char_to_string(char_after)) end def test_display elvar.pop_up_windows = true @x = newbuf(:name=>@bufname, :display=>true) assert(get_buffer_window(@x)) assert_nil(one_window_p) assert_nil(eq(selected_window, get_buffer_window(@x))) end def test_display_pop elvar.pop_up_windows = true @x = newbuf(:name=>@bufname, :display=>:pop) assert(get_buffer_window(@x)) assert_nil(one_window_p) assert(eq(selected_window, get_buffer_window(@x))) end def test_display_only elvar.pop_up_windows = true @x = newbuf(:name=>@bufname, :display=>:only) assert(get_buffer_window(@x)) assert(one_window_p) assert(eq(selected_window, get_buffer_window(@x))) end def test_current @x = newbuf(:name=>@bufname, :current=>true) assert_nil(get_buffer_window(@x)) assert(eq(current_buffer, @x)) end def test_read_only b1 = newbuf(:name=>@bufname, :current=>true, :read_only=>true, :contents=>"a") assert(eq(elvar.buffer_read_only, true)) assert_equal("a", buffer_string) b2 = newbuf(:name=>@bufname, :current=>true, :read_only=>true, :contents=>"c") assert(eq(b1,b2)) assert(eq(elvar.buffer_read_only, true)) assert_equal("c", buffer_string) end def test_bury buf = newbuf(:name=>@bufname, :display=>:pop, :bury=>true) assert(eq(buf, (buffer_list nil)[-1])) end def test_block buf = newbuf(:name=>@bufname, :current=>true) { text_mode } mode = with(:with_current_buffer,buf){elvar.major_mode}.to_s assert_equal("text-mode", mode) end end class TestDefunWithinClass < Test::Unit::TestCase class Foo include ElMixin def initialize(x) elvar.v = x[:value] defun(:twice_v) do elvar.v *= 2 end defun(:str0) do do_str0 x[:str] end end def do_str0(str) (str*2).upcase end end def test0 Foo.new(:value=>10, :str=>"ab") twice_v assert_equal(20, elvar.v) assert_equal("ABAB", str0) end end class TestElApp < Test::Unit::TestCase class Foo < ElApp def initialize(x) elvar.v = x[:value] defun(:twice_v) do elvar.v *= 2 end defun(:str0) do do_str0 x[:str] end end def do_str0(str) (str*2).capitalize end end def test0 Foo.run(:value=>10, :str=>"ab") twice_v assert_equal(20, elvar.v) assert_equal("Abab", str0) end end -- rubikitch http://www.rubyist.net/~rubikitch/index.en.html