Displaying articles with tag

Conditional Compilation of Ruby Code

Posted by rue, Tue Apr 01 23:31:00 UTC 2008

It was one of those silly ideas-nights and I decided to add the ability to have blocks of code that can be omitted from the bytecode depending on a condition as a proof-of-concept. Once the parser is in, we can start working on ‘real,’ more generic sexp and AST manipulators towards something approaching Lisp’s facilities.

I figured it would be a fairly short task and for a change I was correct&emdash;and a good portion of the thanks goes to Rubinius’ already-excellent facilities: I was able to modify the sexp with the full power of Ruby. It took me a bit to figure out the best way to set this up but in the end there are literally about 11 lines of implementation code plus the specs. The end result is a block construct for a special form that allows conditionalising. Currently I only set it up to process global variables but it is trivial to add support for the rest—what may not be trivial is for the user to get other types of conditions set up without confusion over compile-time versus runtime. The invocation is pretty simple:
1
2
3
4
5
6

  # ...

  Rubinius.compile_if($DEBUG) { Logger.debug "Blah blah: #{some_variable}" } 

  # ...
If $DEBUG is false at the time this section goes through the compiler, that entire line disappears from the first R to the last }. If it evaluates to true, the block contents are processed normally&emdash;but we still remove the now-extra wrapper block around it both to avoid the overhead as well as ensure that the variable scope etc. are consistent.

This should be useful for at least removing those generally-unnecessary debug statements etc.; the if $DEBUG does not cost much by itself but it adds up over time. The other easy target are platform/library/etc. specific sections that can be omitted in some cases. It is general-purpose in that the only limiting factor is the condition variable (or possibly expression in the future.)

The commit is here.

0 comments | Filed Under: rubinius | Tags:

Installing JRuby on OS X Using the SoyLatte Java 6 JDK

Posted by rue, Mon Mar 03 01:00:00 UTC 2008

headius often kindly tries to get me to actually overcome my lingering Java allergies to try to run JRuby and now my excuse of FreeBSD amd64 not being Java-friendly has been stripped away.

In reality JRuby of course is a great project running a pretty impressive platform, the JVM, so this should be an interesting experiment. headius suggested using the SoyLatte version of JDK/JRE and looks like not without cause. It is an implementation of Java 6 based off the BSD port. So, steps taken:

  1. Java
    1. Downloaded the amd64 binary from the Soylatte site (Intel Core 2 Duo is, oddly enough, kind_of? amd64—EM64T to be precise)
    2. The directory needs to be extracted somewhere, I chose /opt/local
      1. `cd /opt/local`
      2. `sudo tar xjvpf /path/to/tar.bz2`
      3. `sudo mv long_name soylatte16`
    3. Set the Java environment variables (these go in my ~/.bashrc)
      1. export JAVA_HOME=/opt/local/soylatte16
      2. export PATH=/opt/local/soylatte16/bin:$PATH (front to make sure it overrides system JREs)
      3. I wanted to try NetBeans which expects certain paths: `ln -s /opt/local/soylatte16 /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home`
  2. JRuby
    1. Downloaded the 1.1b binary package
    2. Extracted in /opt/local again
      1. `cd /opt/local`
      2. `sudo tar xzvpf /path/to/tar.gz`
      3. `sudo mv long_name jruby`
    3. `export PATH=$PATH:/opt/local/jruby/bin` to make it visible, ~/.bashrc again
  3. Pick up the new envs: `source ~/.bashrc`
  4. Test: `jruby -v`
  5. Hooray!

Now to see if I can come up with something sensible to use this for.

0 comments | Filed Under: mac | Tags:

ORM Persistence and Thinking Outside the Box

Posted by rue, Fri Feb 08 00:08:00 UTC 2008

I took a bit to completely rewrite the tiny application I had been using (and subsequently neglecting) called Oughtve. I will write more about that later, but a part of the rewrite process was to move away from the semi-binary text files I was using and into a real database. I opted to use SQLite as the backend and for ease of use and (possibly) allowing the user to select a different DB(database) at some point, I wanted to use an ORM. This turned out to be the source of never before seen frustration for me.

Initially I wanted to use DataMapper because its interface seemed to be the best of the bunch. It had an issue with namespaced model names not working (which I will ticket as soon as Lighthouse sees fit to allow me to) but the deeper problem was the inability to define the model classes without being connected to the database which is not good when you are trying to control the database setup phase. Sequel had the same problem if using models. For a brief time, I tried to use Sequel just as a “better SQL” but that got old really quick. Eventually I looked at Og which I must admit I had thought dead only to find it quite vibrant (although very poorly documented.) Og does also, indeed, allow defining the models without an existing database connection which got me past my first annoyance.

The second one really drove me up the wall, though. None of the frameworks has an easy (or any) way of exactly defining the point where the database and schema are created. The situation for destroying the database is the same, and additional problems are created by the general inability to be able to definitively drop the database connection. I went through a huge mess of attempts to be able to run my specs in some deterministic way.

I have to take a lot of the blame for the frustration myself, though. While I think that such features would be immensely useful, in truth I was only trying to do this to be able to run my specs! The setup spec actually does require being able to control the creation but in isolation it is quite possible to do (with just a little bit of stubbing to help.) All the other files would run just fine in isolation, too, but when I tried to run multiple the problems started. I am sure that you already see where I went wrong. Truthfully, I was just immensely annoyed at the entire process at this point and I think it clouded my judgement and caused me to forge ahead, pushing against the walls of the box I had built for myself (with the kind assistance of the ORM frameworks.) It took me entirely too long to put my two ducks in a row.

  1. In the application itself, I do not need the arbitrary setup and teardown (the initial setup can be handled easily enough.)
  2. The specs were only failing when run together—or in other words, in the same process than another spec file.

In hindsight, the solution is obvious and I feel stupid for having bashed my head against the wall for as long as I did. Is it perfect? No. Does it address the ORMs’ shortcomings? No. But it works. So I stopped worrying about it and just wrote a little spec runner of my own that still allows running multiple specs at a time, just in different processes. It is reproduced here for posterity, in case anyone ever needs a template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

#!/usr/bin/env ruby

# The spec files are designed to be run one at a time
# because it removes the necessity of completely tearing
# down the DB setup which is *exceedingly* difficult in
# any ORM.

require 'fileutils'

dummy_dir = File.expand_path "#{File.dirname(__FILE__)}/spec/dummy_dir"

FileUtils.rm_r  dummy_dir, :secure => true  rescue nil
FileUtils.mkdir dummy_dir                   rescue nil

# Split any files from options to pass to the spec program
split = ARGV.index('--')
opts  = if split
          ARGV.slice!((split + 1)..-1).join ' '
        else
          '-f p'
        end

files = if ARGV.empty? 
          Dir.glob("spec/*_spec.rb")
        else
          ARGV.pop if split
          ARGV.map {|f| "spec/#{File.basename(f)}" }
        end

begin
  real_home = ENV['HOME']
  ENV['HOME'] = dummy_dir

  files.each do |spec|
    pid = fork {
      puts "\n#{spec}:"

      exec "spec", opts, File.expand_path(spec)
    }

    Process.waitpid pid
    FileUtils.rm_r  dummy_dir, :secure => true  rescue nil
  end
ensure
  ENV['HOME'] = real_home
end

0 comments | Filed Under: | Tags:

Compiler Walkthrough Illustration

Posted by rue, Fri Nov 09 19:49:00 UTC 2007

Note: this is a sort of a draft. See the real document over in the repo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

# Start
[:block, 
  [:newline, 1, "(eval)", 
    [:defn, :foo, 
      [:scope, 
        [:block, 
          [:args, [:a, :b], [], nil, nil], 
          [:newline, 1, "(eval)", 
            [:yield, 
              [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
            ]
          ]
        ], 
        [:a, :b]
      ]
    ]
  ]
]

# into_script
[:script,  
 [:block, 
   [:newline, 1, "(eval)", 
    [:defn, :foo, 
      [:scope, 
        [:block, 
          [:args, [:a, :b], [], nil, nil], 
          [:newline, 1, "(eval)", 
            [:yield, 
              [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
            ]
          ]
        ], 
        [:a, :b]
      ]
    ]
   ]
  ]
]

# Script.create strips :script
[
 [:block, 
   [:newline, 1, "(eval)", 
     [:defn, :foo, 
       [:scope, 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]
     ]
   ]
 ]
]
# out = [<script node>]


# Script.consume
# convert sexp.first ->
# Block.create comp, sexp
 [:block, 
   [:newline, 1, "(eval)", 
     [:defn, :foo, 
       [:scope, 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]
     ]
   ]
 ]
# out = [<block node>]

# Block#consume sexp.shift
 [ 
   [:newline, 1, "(eval)", 
     [:defn, :foo, 
       [:scope, 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]
     ]
   ]
 ]
# out = [<newline node>]

# Compiler#convert_sexp first
# -> Newline.create
# Newline.new.consume sexp.shift
   [1, "(eval)", 
     [:defn, :foo, 
       [:scope, 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]
     ]
   ]

# << out = [1, "eval", <defn node>]
# compiler.convert <defn node>

# Define.create
# Define.new.consume sexp.shift
# name = sexp[0] (:foo), body = sexp[1..-1]
# scope = super(body)
# ClosedScope#consume body
     [
       [:scope, 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]
     ]
# out = []

# Scope.new.consume sexp.shift
# convert args.first, 
# out = [<converted>, locals]
       [ 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]

# Block.new.consume sexp.shift
# :args => convert
# :newline => convert

         [ 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]


# out = [<args node>, <newline node>] 

# Arguments.new.consume sexp.shift
[[:a, :b], [], nil, nil]

# Newline.new.consume sexp.shift
            [1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
            ] 

0 comments | Filed Under: rubinius | Tags:

More rubinius & a word about rs

Posted by rue, Sun Feb 11 07:34:00 UTC 2007

I set up a new section for all rubinius-related information. All of it may not get posted on the front page here.

In other news, I took a peek at rs again and was pleasantly surprised at how solid the code looks (considering it was written by yours truly) with only a few exceptions. With more time on my hands again, I will try to get a 0.2 out sometime in the near future.

0 comments | Filed Under: | Tags:

Rubinius

Posted by rue, Wed Jan 24 02:13:00 UTC 2007

Recently I have been trying to understand the beast that is Rubinius, a very promising project for writing Ruby in Ruby in the form of a VM and a compiler. Get involved!

1 comment | Filed Under: | Tags:

PAJAMAS

Posted by rue, Wed Sep 27 03:10:00 UTC 2006

AJAX is the current Web 2.0 buzzword. It is, however, terribly precise. The correct term (which everyone should transition to use) is PAJAMAS.

1 comment | Filed Under: | Tags: