zoukankan      html  css  js  c++  java
  • Creating an Interactive JRuby Console for the Eclipse Environment

    Creating an Interactive JRuby Console for the Eclipse Environment

        Musings on Language and Design

        Creating an Interactive JRuby Console for the Eclipse Environment

        by Jeremy Meyer

        April 4, 2008

            Summary

            Excited by the possibilities afforded by JRuby and the ability to plug it into various different Java environments, I decided to try implementing a JRuby interactive console in Eclipse.

        Advertisement


        Need Scala Training or Consulting? Call Escalate. http://www.escalatesoft.com/

        Plugging in JRuby

        Like so many others my
        imagination has been captured by Ruby. Perhaps it is because of the steep rise
        in its awareness the software development community has undergone since Rails,
        perhaps it is a combination of its quirky constructs, enthusiastic (and often
        surreal) proponents, and the fact that DSLs are
        gaining respect and momentum. At the
        same time, the JVM is becoming an obvious platform choice and Java itself is
        starting to look a little tired and lost as a language.

        Perhaps Java is reaching its
        sell-by date, after all, there do seem to be natural universal laws about these things; and
        they apply to a great many domains. Fashion, empires and programming languages
        are three that I can think of which adhere to these rules. They rise, dominate
        and fall. Sometimes they come back, (like flared trousers and interpreters) but
        even if they die, they always leave their legacy, like the Roman
        Empire, or Cobol.

        Anyway, it is obvious that however long it stays in vogue, one such mark that will be left by Java is the JVM as a platform, and fascinated as I am by Ruby, I think that JRuby is
        (together with Rails) what will really keep it on the map. I find myself more
        and more regularly explaining to colleagues and clients (partly to prevent them
        rolling their eyes as one does when faced with a religious fanatic, or a pushy
        salesperson) that I only really love Ruby and crowbar it into every discussion
        I can, because compiled languages like
        Java exist. JRuby makes using Ruby sensible (and
        cool).

        Why is this cool? Because
        like so many consultants, it is my job it is to help people come up with general,
        repeatable solutions to their problems, so I am always on the lookout for some
        sort of lazy reuse idea. Since all clients want those three very simple and clearly
        mutually exclusive features that constantly haunt us, i.e. good, fast and
        cheap, I find myself drawn to the model of leveraging powerful application
        libraries with glue code or something minimal, scriptable and clever.
        JRuby fits that bill quite nicely.
        No Silver Bullet

        I wouldn’t propose anything
        as a silver bullet, but I have seen so many projects fail for the same reasons, that
        I am tempted by anything that can cut out some of the badness.

        This means doing all sorts of things, ranging from trying to improve communication of requirements down to proposing
        better techniques of project management (so agile methodologies are always a
        popular choice). But anything that helps to cut out as many middle-men as
        possible, and allows the people with more domain knowledge to get closer to the engineering of the solutions has to be a good thing.

        Or does it? Isn’t this falling into the old trap of trying getting non-programmers to write programs? Haven’t previous attempts at this
        failed? Languages like BPEL and Visual
        Basic and even (looking back a bit further) Cobol have
        all, in their own way, in their own domains tried to make programming easy.
        These have all ended up producing horrible languages. In the case of the latter
        two they have also produced code bases huge and prevalent by virtue of their
        easy proliferation, not their suitability for solving the problem. Not popular
        with OO purists and lovers of aesthetically pleasing languages!

        Well now we have more languages
        like JRuby(and Jython and
        Groovy etc.) which allow us pleasing constructs, great power and efficient
        syntax together with access to Java libraries and access to any platform which
        has a JVM implementation. More importantly, perhaps, is that this translates into access to any Java middleware. Excellent! This means that
        the powerful application stuff can be written by the software engineers and the
        business logic can be written (at least initially) by the domain experts, using
        a domain specific language (or domain specific flavour, if language isn't subtle enough).
        This should mean that we can give an expert in the domain of feet enough power to shoot
        themselves in the foot with a homing missile, and they wouldn't have to be a rocket scientist.

        What is doubly pleasing of course, is the fact that Ruby by its nature is dynamic (as are some of the other languages which have JVM
        implementations), and building a domain specific
        language to solve a problem can be implemented quite elegantly. So helping
        prevent our domain experts from getting in too much trouble is a bit easier. We can expose the important bits to them and shield the complexity from them. Very importantly though, they will always have the full power of
        the language at their finger tips should they need it.

        Triply pleasing perhaps, is
        the fact that you can do all of this in an interactive shell (jirb in JRuby) if you want to, which is a great and agile way to
        wire up existing domain libraries, or produce "glue code".
        You can embark on a learning adventure with a
        framework, or library and produce a solution that can form the basis of
        something permanent. For example, you
        can experiment with the creation of swing applications using
        JRuby from the interactive shell. This is so easy that even
        a sock puppet could do it (and did, see
        here!).
        Scripting in an Eclipse Environment

        I got to thinking that one
        of the environments it would be great to play with was the Eclipse
        environment. It is
        a mature, fairly solid platform, with a sound plugin model
        and some very powerful development tools available in it. What would be great,
        I thought, would be if you could script in it, or create macros, even. Creating
        an interactive shell would give you access to any plugins
        you liked. Certainly it seemed it would be a worthwhile effort plugging in an
        interactive shell to see what would happen. As a self-confessed
        non-expert-but-fascinated wannabe Ruby-ist, I thought
        I could kill two birds with one (precious) stone, i.e. learn more about the language
        and learn more about the Eclipse platform at the same time.

        How did I do this? Some of
        the highlights follow below. If you
        really can’t bear the thought of looking through code, or want to see
        everything in its entirety, then download this zip which has the Eclipse
        plugin and the full source included:

        The obvious place to start looking
        at how to do this was jirb,
        the interactive Ruby shell written for JRuby. Of
        course, I thought, this would be a Java console that read in a line of text and
        passed it to the Ruby interpreter for evaluation, so it couldn’t be too hard to
        re-implement this in an Eclipse app.



        Turns out I was slightly wrong. The jirb command, it seems, is just a batch file that runs
        JRuby and points it to irb. So irb, the interactive Ruby shell,
        is just a Ruby program and jirb ,
        the interactive JRuby
        shell is just irb
        running in JRuby.
        The Ruby Code

        After some head scratching,
        I realised that this was very cunning and would actually help make my life
        easier. I did some investigation into the irb code, and with help from a good
        (albeit old) article
        by Leonard Richardson about unit testing the Ruby Cookbook source code, I
        discovered that redirecting irb input and output is fairly straightforward. Turns out that
        all you need to do is extend the Ruby Irb class and
        get it to use our own input and output streams. The Irb
        class implements the strategy pattern to read from its input stream using an
        InputMethod. Creating an Input Method is as simple as
        providing a Ruby class which has a gets operation and a prompt= setter method. The
        prompt= setter method is necessary, because irb will throw exceptions
        without it, (although I admit I am stumped as to why it is there, it doesn’t
        seem to do anything other than pass in an empty string.)

        I needed to customise my prompt,
        and get the line of text typed, so:

        

        class EclipseConsoleInputMethod
          # echo the prompt and get a line of input.
          def gets
            $stdout.print 'eIrb:> '
            $stdin.gets
          end

          def prompt=(x)
          end
        end

        

        And now, I could minimally extend
        the Ruby Irb class to give me a custom
        Irb class, which has the right context and some useful
        configurations:

        

        class EclipseConsoleIrb < IRB::Irb

          def initialize(ec_inputmethod)
            IRB.setup(__FILE__)
            IRB.conf[:VERBOSE] = false
            super(nil, ec_inputmethod)
          end

          def run
            IRB.conf[:MAIN_CONTEXT] = self.context
            eval_input
          end
        end

        

        Not too hard at all!
        Now what I needed to do was get an Eclipse
        console to provide said input and output streams and pass the typed Ruby code into
        the former and echo the results into the latter.
        The Java Code

        Eclipse’s IO Console does that
        job very well, it displays in the normal Eclipse Console area, provides and
        input stream for you, and can have multiple output streams directed to it, perfect.
        (You can even set colours for the different streams!)

        I created a simple Eclipse
        Plugin application using the basic Eclipse "new project" wizard.
        All I had to do was create a subclass of an IOConsole
        that had an instance of the RubyInterpreter class in
        it ..

        Highlights below:

        

        import org.jruby.Ruby;

        public class RubyConsole  extends IOConsole implements Runnable {

           public void run() {
              ..
              RubyInstanceConfig conf = new RubyInstanceConfig() {
                 public InputStream getInput() {return in;}
                 public PrintStream getOutput() {return out;}
                 public PrintStream getError() {return err;}
                 ..
              }
              ..
              try {
                 Ruby rubyRuntime = Ruby.newInstance(conf);
                 String jRubyHome = System.getProperty("jruby.home");
                 String jRubyVersion = System.getProperty("jruby.version");
                 rubyRuntime.evalScriptlet("$:.insert(0,"+jRubyHome+"\\lib\\ruby\\"+jRubyVersion+"')");
                 rubyRuntime.evalScriptlet("$:.insert(0,'"+jRubyHome+"\\lib')");
                 rubyRuntime.evalScriptlet("require 'jruby';");
                 rubyRuntime.evalScriptlet("require 'eclipse_console_irb';");
              } catch (Exception e) {
                 e.printStackTrace();
              }
           }
        }

        
        I got the input output and
        error streams from the superclass IOConsole,
        and used them to create an inner config class of type
        RubyInstanceConfig. I then used that to make the Ruby
        Interpreter. You will notice that once I
        create the new interpreter I call the evalScriptlet
        method with some Ruby script code. First I add the paths to the Ruby load path
        by inserting into the special Ruby array $: . I then
        issue two require statements. One to start
        JRuby and one to load up my Eclipse console Ruby script,
        which contains the Ruby code already shown above.

        The Console can be added to
        the GUI by a very simple piece of code. I chose to add it by creating an action
        that creates a new Console, so I have a new menu
        entitled Ruby Console

        Highlights below:

        

        ..
        static RubyConsole ruby = new RubyConsole();
        ..
        ConsolePlugin.getDefault().
           getConsoleManager().addConsoles(new IConsole[]{ ruby });
        ConsolePlugin.getDefault().getConsoleManager().showConsoleView(ruby);
        ..

        

        And that is it.
        There really wasn’t much more to it than
        that, so when I say highlights I actually mean almost everything. Most of
        what I have left out is the Eclipse plugin library
        code. All the Ruby code I needed is shown above.

        After I build the
        plugin from my project, and deploy it, I get the rather
        pleasing result of a Ruby console in my Eclipse workbench with
        a eIrb>: prompt at the
        console, and the interpreter’s results shown as I type in commands. It even has
        nice colours. Shown here:






        The Result

        What can you do with this
        though? Well anything you like (within
        reason) but provided your plugin has added upstream
        plugins to its dependency list, you can load any Java
        classes you like from that plugin and work with them.
        For example, I have added the “org.eclipse.core.resources”
        to my RubyConsole plugin’s
        dependency list, and so I can access the Eclipse Workspace by typing:

        eIrb:> include_class 'org.eclipse.core.resources.ResourcesPlugin'

        irb responds with:

        => ["org.eclipse.core.resources.ResourcesPlugin"]

        Indicating that I have loaded up the class..

        eIrb:> workspace = ResourcesPlugin.get_workspace

        Yields:

        => #<Java::OrgEclipseCoreInternalResources::Workspace:0x25abb1 @java_object=org.eclipse.core.internal.resources.Workspace@12605d>

        So I now have the workspace object stored in workspace. The great thing about this environment of course, is that should I feel I can’t be bothered to look in the Javadocs for the methods available on the Eclipse Workspace class, I can make use of Ruby’s reflection by just typing:

        eIrb:> workspace.methods

        Which returns a huge array of all the methods (too big to list here). I can then experiment with the sensible looking ones, and soon discover that:

        eIrb:> projects = workspace.get_root.get_projects
        eIrb:> projects.each { |p|
        eIrb:>     puts p.get_name
        eIrb:> }

        ..will list all of the names of the projects in my Eclipse workspace, while:

        eIrb:> projects[0].build(0,nil)

        ..will force the first project in the workspace to build.

        eIrb:> projects[0].close(0,nil)

        ..will close the project, while:

        eIrb:> workspace.get_root.get_project('project1').open nil

        .. would open a project in my workspace called ‘project1’. Of course this is not good coding style at all, and if you didn’t have a project with this name, you would get a Java exception reported at the Ruby prompt, but you get the idea.

        All in all, this is a really agile way to develop and a great way to test and experiment with libraries and middleware. Of course, once you have a piece of Ruby code that does something useful, you can save it in a .rb file and use the Ruby’s require statement at the prompt to load it up. You would then be able to continue typing interactively.

        eIrb:> require 'useful.rb'

        I hope you have fun playing around with this. Enjoy!

  • 相关阅读:
    vs 2015 "加载该页时出错。" 解决方案
    Web API使用HttpResponseMessage与HttpResponseException的差异 HttpResponseMessage 返回类型
    a标签使用href=”javascript:void(0); 在火狐浏览器跟chrome 不兼容
    SQL语句 转
    Fiddler抓包 截包伪造提交包
    图片懒加载
    dropzone 上传插件
    MVC5+EF6 完整教程 转
    用Aspose.Cell控件导入Excel非强类型的数据
    利用Aspose.Word控件实现Word文档的操作
  • 原文地址:https://www.cnblogs.com/lexus/p/2370704.html
Copyright © 2011-2022 走看看