[ANN] el4r-0.9.2 - EmacsLisp for Ruby

R

rubikitch

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

=== 0.9.1 -> 0.9.2

* ~/.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.2.tar.gz").read.display' | tar xzf -
cd el4r-0.9.2

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 <[email protected]>
# Version: $Id: test-el4r.rb,v 1.34 2005/10/04 19:33:31 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 = <<EOB
nil
=> 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)

return
# FIXME! this form works incorrect even typing `C-x C-e'.
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
# 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=>:eek:nly)
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,961
Messages
2,570,131
Members
46,689
Latest member
liammiller

Latest Threads

Top