RailsConf EU, part 3


Drat. I missed _why’s keynote speech. Oh well, I hear they recorded everything in the congress hall, so now I’m just waiting for a podcast of it. What I didn’t miss was Jim Weirichs keynote which was really great. It was about some of Ruby’s more dangerous features and how to avoid people getting really mad at you when using them in a library. Like, for example changing the semantics of Kernel#require. Or making Logger singleton. And other fun stuff. The tips where quite basic, common sense things that Rubyists really need to think more about. For example; don’t pollute the global namespace with classes named Node. Or name your namespace HTML. The other part of the speech focused on Design by Contract in the Bertrand Meyer sense. This was the watered-down version, looking at preconditions on method arguments and postconditions on the return value. I would have liked seeing something more about preconditions, postconditions and invariants on objects too, since this is a really important part of DoC in my opinion. But it was still great.

I had to run before _why’s speech and only came back in the middle of the MySQL optimization talk. (The reason for leaving will probably be obvious in one or two weeks time.)

Now, MySQL optimization… Oh wow, I’m twisted. I used to like MySQL. But now I have a really big problem with it, and especially in the Rails crowd. Actually, after David Blacks session my problems with MySQL and databases are so big I really have to spend an entire blog entry for it. Coming up.

Anyway, I can’t say much about the session on MySQL exception: “What are you smoking, man, and can I have some of that?” to quote DHH in an unrelated talk. This guy from MySQL actually said some really nasty things about PostgreSQL and DB2 since (in his opinion) they were less SQL compliant than MySQL. Oh wow. Oh wow. I’m really upset about this, so I’ll quickly continue writing about the next session.

Oh yeah, a small interlude to all Rubyists out there. Charles pointed something out to me the other day in London and I can’t stop thinking about it. What he asked me was this: “Have you ever seen a unit test attached to patches submitted for Ruby-core?”. No, I haven’t, and it scares me. Really much. Anyone wants to inject an opinion on this matter?

Talking about Charles Nutter; his JRuby talk was next up and boy was it great? I know what it would contain but it still is really nice to see. JMX control over Rails. EJB session beans feeding data into Rails. JDBC database backend (was MySQL, could’ve been Apache Derby, Mimer SQL or something entirely other). All of this, with such small amounts of code. I really got the impression that the Ruby crowd actually grok why the JVM can give them even more power now. I felt a big change in the climate. No more Java bashing. Things are moving forward.

James Duncan Davidsson ended RailsConf for me, since I had to move towards Heathrow at the moment he finished. He talked about how to scale our applications in a different way, using techniques we can only guess at that Google and Amazon has deployed internally. Big, clustered memory databases and distributed file systems on a scale not really possible for most of us right now. It was interesting and it got me very excited about the close future.

I didn’t get to see Dave Thomas either, but I’m counting on the podcasts once again. All in all, it’s been two great days. Very intense, very fun and really interesting. It’s been great meeting all of these people, especially seeing Charles again, talking to Jim Weirich and why the lucky stiff. It’s been great. When’s next time?



RailsConf EU, part 2


After watching the Rails speaks C-session, I went to lunch. Got back to see the session on Capistrano. Sadly, it wast most about the new features and not much at all about the basics of Capistrano. If I understood the audience correctly, most were expected something like that.

Next session for the day was Camping: Going of the Rails with Ruby, by Eleanor McHugh. She had a companion with her, but I don’t remember his name. Actually, the presentation was really crazy. It was good fun, really crazy, but I’m not sure I actually learned so much. It was fun that she’d included that JRuby runs Camping in her presentation.

Next session was David Black’s Database Engineering and Rails, which more or less stated loads of questions and undecided issues regarding databases and Rails. There are many things people aren’t talking about, and DB design is one of them. Davids talk was mostly questions and a general idea that we need to do better in this area.

Last session for the day was Ugo Cei’s talk about Ruby for Java programmers. As Ugo noted in the beginning, there were no abstracts posted on the schedule, and the title for his talk was slightly confusing because of that. The presentation was about getting Ruby and Java to inter operate, not to compare languages to each other, or learn Java programmers how to code Ruby. As such, I had known this from before but my colleagues didn’t, and were pleasantly surprised. The presentation was really good. Ugo did a great job of detailing all different approaches, the pros and cons of each and also showed some demos, including my JRuby Camping. Of course, the demo demon was there, and the RubyGems bug surfaced alot.

Oh, and as a side note, that issue will hopefully soon be fixed. Charles and me talked with Jim Weirich about it yesterday, and we may be close to a solution.

After the days session I went back to my hotel to freshen up a little bit. I then went back to the conference center in time to see the Rails Core Team panel discussion. This was quite interesting, but the question I wanted to have answered didn’t make it into the session, since I submitted it to late. A few interesting tidbits of information was given. First, SimplyHelpful made some people afraid there would be div_for-helpers all over the place, giving rise to much overhead in terms of unnecessary CSS classes and ids. This is not the case, since div_for should only be used for special cases where the repetition is apparent. Another interesting issue was the proliferation of globalization and internationalization plugins for Rails. The question was whether the core would some day contain these things. The answer is a clear no, but some things need to be better in the core, for example making strings available to change.

The question I wanted to have answered was if ActiveRecord someday will be refactored or rewritten. There are two reasons I see for doing this; to remove the MySQL-specific assumptions from the AR core, and to make it possible to have Prepared Statements. Alas, I didn’t get answers to this.

After that, DHH ended the session by spending 20 minutes ranting about the recent security issue and more interesting; the communities reaction to this issue. His core point is simple: he doesn’t owe anyone anything just for downloading and using Rails. If you have contributed to the community or Rails that’s another thing entirely; that means you’re vested in the framework and entitled to some information. It was really interesting. I hadn’t realized how irritated DHH had become by those discussions.

Oh well. Now it’s time for Jim Weirich and Why The Lucky Stiff to hold a general session. It’s bound to be interesting. More reports later.



RailsConf EU, part 1


About ten minutes ago, Kathy Sierra finished her part of the plenary session for RailsConf. She was preceded by DHH and they talked for about an hour each. Of course, DHH’s talk was quite technical and mostly about Rails 1.2 and 2.0. Let me tell you, there are some really awesome stuff there. I really liked SimplyHelpful which is smart helpers for your view, mostly like ActiveRecord for models and SimplyRestful for controllers. It makes repetitions disappear in a very pleasing way. There were of course lots of other small things talked about. ActiveResource looks really nice. I just wish I had an interface like that for the SOAP servers I work against. Oh well.

Kathy Sierra. Wow. Really interesting talk. Incredible actually. She talked about creating passion in users, how you can work with selling your product to people who are already customers in such a way that they become passionate and really make your product advertise itself. She talked much about how our brains work regarding these topics and how you can “cheat” to grab attention. A very telling point in her speech was when she showed the Canon advertisement for one of their cameras, and then their manual for those who had actually bought the camera. Of course, the manual was sleep inducing and probably bad for your health just to look at, but the advertisement was cool, “sexy” and all those ad buzzwords.

The current session I’m in is about Rails and C, and how you can apply C extensions to make your applications blazing fast. This is a really appropriate topic if you look at the current war between Joel Spolsky (Language Wars, Wasabi, Ruby Performance) and DHH (FUD, Wasabi and Performance). Also, the last few months have seen many discussions on ruby-talk about performance (or lack of) in Ruby. David Goodlad starts the session by speaking about different ways of interfacing with C. The ways detailed are regular C extensions, Ruby/DL, RubyInline and SWIG.
An interesting take on the Rails issue, and how to get great performance from one server running mostly C code and the then have your 5 Rails boxes talking to this with BackgrounDRb. Really neat.



London arrival


My London arrival yesterday was really abysmal. Our flight was two hours late and when we finally arrived at our hotel (Russell Hotel: never live there), they told us one of our five reserved room was damp. So, we told her to fix another hotel close to the conference center. At that point she gave our rooms to somebody else. As if that was not enough, they were incredibly rude and tried to blame “management” for what had happen. A management that conveniently wasn’t around and would talk to us on phone.

Finally, after two hours waiting, we got rooms at Hilton Waldorf. When I finally got to bed the clock was 3am local time and I was to stressed out to get any restful sleep.

The good part of the sleeplessness is that I had time to do some hacking. I’ve implemented support for Mimer SQL in ActiveRecordJDBC and also fixed a small error that Peter Chan found while trying it out.

For those who don’t know about it, I really recommend Mimer. It’s a great database, really fast and extremely focused on complete SQL compliance in all ways.

I’m writing this a few minutes after the first plenary session for RailsConf was supposed to start. This will be interesting. I have already said hi to Charles, and it looks to be two interesting days. Envy me! =)



My London Schedule


Tomorrow evening (Wednesday), me and my colleagues will land in London for RailsConfEU. It will be a hectic time but I’m counting on many interesting presentations. I will post my current schedule here, so people interested in saying hi can know where I will be.

  • Arrival: We land at Heathrow airport 20:15 GMT. This means I will not be at Pizza on Rails (sadly enough).
  • Thursday: RailsConf starts. I’m planning on attending sessions all day from 8:30 to 20:30. I’m not sure exactly which ones yet, but there are many interesting ones (and some conflicting). I plan on looking at David Black’s talk on Database Engineering and Eleanor McHugh’s session on Camping (since I have a special interest in Camping). Ugo Cei’s talk on Ruby for Java will be most interesting too.
  • Thursday evening: A friend will show me the night life in London.
  • Friday morning: I’m planning on going to the plenary session but I won’t go to the first sessions after that.
  • Friday lunch time: Lunch and an hour of sightseeing with Meg.
  • Friday afternoon: I’m not sure which sessions to go to. I’m a little bit mad about the planning that conflicts the four most interesting sessions at the same time. I’ll go see Charlies JRuby-presentation, though.
  • Friday evening: We fly from Heathrow at 20:00 GMT, so, once again, we won’t be able to go to the JRuby meet up.


First the Sun, then the world


As has been announced at various places today, Sun has made it official that they hire Charles O Nutter and Thomas Enebo to work full time on JRuby and Ruby development tools. This is obviously great news for JRuby fans, Ruby evangelists and Java developers. Because this news is something we all will benefit from.

So, what does this mean to JRuby, Ruby and Java? Well, JRuby will gain even more traction. It’s fair to say that the next few months probably will see an explosion of fixes to JRuby correctness and performance. I’m willing to bet money that JRuby will run Rails as fast as MRI by New Years. Of course, this is something that the Ruby language will benefit greatly from. JRuby won’t be a competitive Ruby implementation; it will be a complementary one where the resources of Java will be available to systems that need the agile development process of Ruby. Other clear benefits is the possibility of creating a real Ruby language specification and test suite.
For Java the issue isn’t as clear cut. Java the language will probably lose developers to Ruby. But they won’t move from the Java platform; and this is the important point. It’s the platform that counts; and Ruby with the power of the Java platform will be something incredible to behold.

I guess the next question for me and other JRuby developers will be what next? Should we lie down on the couch, take a rest and let the big guys take care of JRuby from now on? Of course not. Actually, the situation is the complete inverse. It is now we should make an extra effort. It is now that we are needed. Two great developers working full time will not be enough. It will never be enough. This is really a call to arms. Now is the time for all those developers that I know are out there, thinking about pitching in, but haven’t decided to do it yet. Now is the time to help. There are test cases to be written, documentation is needed. There are core changes that need to be implemented. There are extensions to write, and applications to test. We have one gem released on RubyForge that demands JRuby. In a years time there should be one hundred!



ActiveRecord-JDBC 0.2.0


Earlier today I finally released version 0.2.0 of ActiveRecord-JDBC. Since it contains fairly big changes I decided to bump the version number a bit, from 0.0.1 to 0.2.0. This blog entry will first describe what’s new and cool about this release, and then show how to get started with this adapter inside Rails.

What’s new
Of course, to warrant a new release, there’s got to be some good stuff in it, and I can really say I’m proud of the funcionality included in this package. I feel that the first release was much proof-of-concept, and to make it easy for people to follow along in the Camping tutorial. But with this release, ActiveRecord-JDBC is definitely an option for a real project.

ActiveRecord-JDBC (AR-JDBC from now on) ships with support for 8 databases, including migrations. Adding support for more is often very trivial and we hope to very soon have support for most major databases. What works right now is:

  • MySQL
  • PostgreSQL
  • Oracle
  • HSQLDB
  • Microsoft SQL Server (except for change_column_default)
  • DB2 (except change_column, change_column_default, rename_column, remove_column,
    add_index, remove_index and rename_table)
  • Derby (except change_column, change_column_default, remove_column, rename_column)
  • FireBird (except change_column_default and rename_column)

The exceptions in this list will probably not be fixed, since there is no support in the database for this functionality. Of course, some of it could be faked by expensive copy-table operations, but this seem to be detrimental since it often doesn’t work for sequences and identity columns. We felt it was better to just don’t support renaming tables in those drivers.

Putting JDBC on Rails
Now, I will detail how to create a basic Rails application using JRuby and AR-JDBC. I assume you have a working JRuby installation, and have installed Rails. Since I have rails and gem in my global path from regular Ruby, I have added symlinks in $JRUBY_HOME/bin to make it easier using these commands. Specifically, I will use jem for gem, jails for rails and jake for rake. This makes the command lines very much shorter indeed.

The first step is to install the AR-JDBC gem: (we don’t generate rdoc and ri since these are still expensive operations in JRuby)

jem install -y ActiveRecord-JDBC –no-ri –no-rdoc

After this operation has completed, the next step is to create our rails-application:

jails test_arj

This will take a while and generate the usual files. Now, to actually make Rails use the JDBC-connector, we need to change environment.rb to load our jdbc_adapter. There’s a thread on the rails-core-list about making this step unnecessary, but right now it’s needed. Anyway, modify the head of the file to look like this:

require File.join(File.dirname(__FILE__), ‘boot’)
require ‘jdbc_adapter’
Rails::Initializer.run do |config|

that is, add a require for the jdbc adapter. The next step is to configure your database. I will the Derby embedded driver for this, but the concept is the same; just change the adapter name, add driver, url and possibly username and password. This is the configuration any JDBC driver takes so it shouldn’t come as a surprise for Java developers. Make sure the driver is on your CLASSPATH before running anything, though. My development-database specification looks like this:

adapter: jdbc
driver: org.apache.derby.jdbc.EmbeddedDriver

url: jdbc:derby:test_arjdbc;create=true

and you see here that there isn’t anything strange going on, we just configure the JDBC driver with the same parameters as usual.

From now on, the operations are the same as when creating a regular Rails application. First, create the model:

jruby script/generate model Product

and then edit db/migrate/001_create_products.rb by adding this:

t.column :title, :string
t.column :description, :text
t.column :image_url, :string
t.column :price, :float

Nothing strange. When this is done, we can create our data:

jake db:migrate

which, since we’ve chosen to use Derby, will create some database files in the current directory. This is what I want for now. After this step, some scaffolding is in place:

jruby script/generate scaffold product

You will notice we have to use jruby explicitly for some scripts. This is because the shebang doesn’t get set correctly for these files.

After this step I copied my development configuration to the production configuration in the database and started webrick in production mode (since it’s much faster right now):

jruby script/server -e production

Now you should be able to visit http://localhost:3000/products and going wild crudding your products.

… And the rest? There is nothing more. From here on, it’s a regular Rails application, just with the added benefit of all the Java resources in the world available to you.



RailsConf Europe


So, it has finally been decided. Me and my four colleagues will go to London for RailsConf. It feels really good, especially since the lineup with speakers is great.

Hopefully I’ll get the chance to meet up with some people too. I know at least one JRuby’ist who’ll be there.



Timeline for the next few weeks.


  • JvYAML:
    I have finally been able to actually get some work done on JvYAML again. The plan is to have the 0.2 release out in two weeks time. There are three components left to finish. I have already completed the Emitter and the Serializer. What’s missing is the Representer, JavaBean support and JRuby integration. But when this is finished, we will have a complete pure Java YAML library for JRuby
  • Mongrel:
    I really want Mongrel to work in JRuby. We have been spending some time getting the HTTP-parser ported to Java, and this is more or less finished now. Earlier today I managed to run all the Mongrel test cases for the parser successfully in JRuby, so we’re clearly on the way. What we need right now is to get the first parts of the Extension API into JRuby proper. When that’s done we can start testing Mongrel for real.
  • ActiveRecordJDBC:
    I have a goal to rewrite parts of this to abstract away differences between different drivers (we have some specialized code for Oracle, some for MySQL, and that’s no good in the long run.) I’m also planning on writing some test cases using Migrations, so we can test the driver against many different databases. What I really want to do is to test MySQL, Oracle, MS SQL Server and PostgreSQL enough to say that JRuby can use them. Especially MS SQL would be good, since MRI only can use ODBC if not running production on a Win32 machine.
  • JRuby performance and bug correction:
    Right now these are the big points. We need to correct as many bugs as possible, nibble away at performance, and also improve documentation. I will try to take a look at these issues if time allows.
  • CLaJ:
    This is a new project that I have been thinking about and planning for the last few months. I won’t say much more about it right now, but that it is something I’m really passionate about (even more than JRuby). Expect more information in a blog post soon.


The JRuby Tutorial #2: Going Camping


So, for the 2nd, (actually third installment, but the JIRB one doesn’t really count), I was thinking that we should take a look at a few different subjects. The red thread will be a simple web based Blog application built with Camping. Camping is a microframework for web development, by whytheluckystuff, is insanely small and incredibly powerful. It uses another library called Markaby, which generates markup based on pure Ruby code. I will show the application in smaller parts, with explanations, and at the end include a link to the complete source file.

First of all we have to have a working JRuby built from the latest version of trunk. After JRuby is working we need to install a few gems. Follow these commands and you’ll be good:

jruby %JRUBY_HOME%\bin\gem install camping –no-ri –no-rdoc –include-dependencies
jruby %JRUBY_HOME%\bin\gem install ActiveRecordJDBC –no-ri –no-rdoc

This installs Camping, ActiveRecord, ActiveSupport, Markaby, Builder, Metaid and ActiveRecordJDBC. We don’t generate RDoc or RI for these gems, since that’s one part of JRuby that still is pretty slow.

The Blog application
The first thing the blog application needs is a database. I will use MySQL, but other databases may work through the JDBC-adapter, but there is still some work to be done in this area. I will have my MySQL server on localhost, so change the configuration if you do not have this setup. You’ll need a database for the blog application. I’ve been conservative and named the database “blog” and the user “blog” with the password “blog”. Easy to remember, but not so good for production.

Update: As azzoti mentioned, you have to set your classpath to include the MySQL JDBC-driver, which can be downloaded from http://www.mysql.org/.

Now, open up a new Ruby file and call it blog.rb. The name of the file is important; it has to have the same name as the Camping application. Now, first of all we include the dependencies:

require rubygems
require ‘camping’
require ‘camping/session’
require_gem ActiveRecordJDBC
require ‘active_record/connection_adapters/jdbc_adapter’
require ‘java’

include_class ‘java.lang.System’

These statements include Rubygems, Camping, Camping session support, ActiveRecordJDBC and Java support. It also includes the Java class named java.lang.System for later use. The next step is to include some configuration for our application.

Camping.goes :Blog
Blog::Models::Base.establish_connection :adapter => 'jdbc', :driver => 'com.mysql.jdbc.Driver',
:url => 'jdbc:mysql://localhost:3306/blog', :username => 'blog', :password => 'blog'

module Blog
include Camping::Session
end

These statements first names our application with the Camping.goes-statement. This includes some fairly heavy magic, including reopening the file and rewriting the source to include more references to the Camping framework. But this line is all that is needed. The next line establishes our connection to the database, and it follows standard JDBC naming of the parameters. Of course, these should be in a YAML file somewhere, but for now we make it easy. The last part makes sure we have Session support in our Blog application.

Now we need to define our model, and this is easily done since Camping uses ActiveRecord:

module Blog::Models
def self.schema(&block)
@@schema = block if block_given?
@@schema
end

class Post < Base; belongs_to :user; end
class Comment < Base; belongs_to :user; end
class User < Base; end
end

The first part of this code defines a helper method that either sets the schema to the block given, or returns an earlier defined schema. The second part defines our model, which includes Post, Comment and User, and their relationships.

The schema is also part of the application, and we’ll later see that Camping can automatically create it if it doesn’t exist (that’s why we didn’t need to create any tables ourselves, just the database).


 Blog::Models.schema do
create_table :blog_posts, :force => true do |t|
t.column :id, :integer, :null => false
t.column :user_id, :integer, :null => false
t.column :title, :string, :limit => 255
t.column :body, :text
end
create_table :blog_users, :force => true do |t|
t.column :id, :integer, :null => false
t.column :username, :string
t.column :password, :string
end
create_table :blog_comments, :force => true do |t|
t.column :id, :integer, :null => false
t.column :post_id, :integer, :null => false
t.column :username, :string
t.column :body, :text
end
end

This defines the three tables needed by our blog system. Note that the names of the tables include the name of the application as a prefix. This is because Camping expects more than one application to be deployed in the same container, using the same database.

When we have defined the schema, it’s time to define our controller actions. In Camping, each action is a class, and each action class define a method for get, one for post, etc. These classes will be defined inside the module Blog::Controllers. The first action we create will be the Index action. It looks like this:


     class Index < R '/'
def get
@posts = Post.find :all
render :index
end
end

This defines a class that inherits from an anonymous class defined by the R method. What it really does, is bind the Index action to the /-path. It uses ActiveRecord to get all posts and then renders the view with the name index.

The Add-action adds a new Post, but only if there is a user in the @state-variable, which acts as a session. If something is posted to it, it creates a new Post from the information and redirects to the View-action:


     class Add
def get
unless @state.user_id.blank?
@user = User.find @state.user_id
@post = Post.new
end
render :add
end
def post
post = Post.create :title => input.post_title, :body => input.post_body,
:user_id => @state.user_id
redirect View, post
end
end

As you can see, there’s not much to it. Instance variables in the controller will be available to the view later on. Note that this action doesn’t inherit from any class at all. This means it will only be available by an URL with it’s name in it.

We need a few more controllers. View and Edit are for handling Posts. Comment adds new comments to an existing Post. Login and Logout are pretty self explanatory. And Style returns a stylesheet for all pages. Note that Style doesn’t render anything, it just sets @body to a string with the contents to return.


     class View < R '/view/(\d+)'
def get post_id
@post = Post.find post_id
@comments = Models::Comment.find :all, :conditions => ['post_id = ?', post_id]
render :view
end
end

class Edit < R '/edit/(\d+)', '/edit'
def get post_id
unless @state.user_id.blank?
@user = User.find @state.user_id
end
@post = Post.find post_id
render :edit
end

def post
@post = Post.find input.post_id
@post.update_attributes :title => input.post_title, :body => input.post_body
redirect View, @post
end
end

class Comment
def post
Models::Comment.create(:username => input.post_username,
:body => input.post_body, :post_id => input.post_id)
redirect View, input.post_id
end
end

class Login
def post
@user = User.find :first, :conditions => ['username = ? AND password = ?', input.username, input.password]
if @user
@login = 'login success !'
@state.user_id = @user.id
else
@login = 'wrong user name or password'
end
render :login
end
end

class Logout
def get
@state.user_id = nil
render :logout
end
end

class Style < R '/styles.css'
def get
@headers["Content-Type"] = "text/css; charset=utf-8"
@body = %{
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
body {
font-family: Utopia, Georga, serif;
}
h1.header {
background-color: #fef;
margin: 0; padding: 10px;
}
div.content {
padding: 10px;
}
div.post {
background-color: #ffa;
border: 1px solid black;
padding: 20px;
margin: 10px;
}
}
end
end

Also note how easy it is to define routing rules with the help of regular expressions to the R method.

Next up, we have to create our views. Since Camping uses Markaby, we do it in Ruby, in the same file. Views are methods in the module Blog::Views with the same name as referenced inside the controllers call to render. There is a special view called layout which get called for all views, if you don’t specify otherwise in the call to render. It looks like this:


     def layout
html do
head do
title 'Blog'
link :rel => 'stylesheet', :type => 'text/css',
:href => '/styles.css', :media => 'screen'
end
body do
h1.header { a 'Blog', :href => R(Index) }
div.content do
self << yield
end
end
end
end

As you can see, standard HTML tags are defined by calling a method by that name. The contents of the tag is created inside the block sent to that method, and if it makes sense to give it content as an argument, this works too. Title, for example. If you give a block to it, it will evaluate this and add the result as the title, but in this case it’s easier to just provide a string. Note how a link is created, by the method R (another method R this time, since this is in the Views module). Finally, the contents of the layout gets added by appending the result of a yield to self.

The index view is the first we’ll see when visiting the application, and it looks like this:


     def index
if @posts.empty?
p 'No posts found.'
else
for post in @posts
_post(post)
end
end
p { a 'Add', :href => R(Add) }
p "Current time in millis is #{System.currentTimeMillis}."
end

I’ve also added a call that writes out the current time in milliseconds, from Java’s System class, to show that we’re actually in Java land now, and potentially could base much of our application on data from Java. We check if there are any posts, and if so iterate over them and write them out with a partial called _post. We also create a link to add more posts. The rest of the views look like this:


     def login
p { b @login }
p { a 'Continue', :href => R(Add) }
end

def logout
p "You have been logged out."
p { a 'Continue', :href => R(Index) }
end

def add
if @user
_form(post, :action => R(Add))
else
_login
end
end

def edit
if @user
_form(post, :action => R(Edit))
else
_login
end
end

def view
_post(post)

p "Comment for this post:"
for c in @comments
h1 c.username
p c.body
end

form :action => R(Comment), :method => 'post' do
label 'Name', :for => 'post_username'; br
input :name => 'post_username', :type => 'text'; br
label 'Comment', :for => 'post_body'; br
textarea :name => 'post_body' do; end; br
input :type => 'hidden', :name => 'post_id', :value => post.id
input :type => 'submit'
end
end

And the three partials:


     def _login
form :action => R(Login), :method => 'post' do
label 'Username', :for => 'username'; br
input :name => 'username', :type => 'text'; br

label 'Password', :for => 'password'; br
input :name => 'password', :type => 'text'; br

input :type => 'submit', :name => 'login', :value => 'Login'
end
end

def _post(post)
div.post do
h1 post.title
p post.body
p do
a "Edit", :href => R(Edit, post)
a "View", :href => R(View, post)
end
end
end

def _form(post, opts)
p do
text "You are logged in as #{@user.username} | "
a 'Logout', :href => R(Logout)
end
form({:method => 'post'}.merge(opts)) do
label 'Title', :for => 'post_title'; br
input :name => 'post_title', :type => 'text',
:value => post.title; br

label 'Body', :for => 'post_body'; br
textarea post.body, :name => 'post_body'; br

input :type => 'hidden', :name => 'post_id', :value => post.id
input :type => 'submit'
end
end

In my opinion, this code is actually much easier to read than HTML and most of it is fairly straight forward. One interesting part is the add and edit methods, which checks if a user is logged in, otherwise uses the _login-partial instead of showing the real content.

Finally, we will define a create-method for Camping, which is responsible for creating the tables for our model:


 def Blog.create
Camping::Models::Session.create_schema
unless Blog::Models::Post.table_exists?
ActiveRecord::Schema.define(&Blog::Models.schema)
Blog::Models::User.create(:username => 'admin', :password => 'camping')
end
end

This first creates a table for the session information, and then checks if the Post-table exists; if not all the tables in the schema defined before is created.

Now you have seen the complete application. If you have no interest in writing this by hand, the complete code can be found here.

Running the application
To run a Camping application, you need to run the camping executable that has been installed into your %JRUBY_HOME%\bin on your application file. In my case I run it like this:

jruby %JRUBY_HOME%\bin\camping blog.rb

in the directory where my blog.rb exists and I very soon have a nice application at http://localhost:3301/blog which works wonderfully. Startup is a little bit slow, but as soon as WEBrick has started listening the application is very snappy. You can try changing your blog.rb-file too; Camping will automatically update your application without having to restart the server. As I said above, I included a call to System.currentTimeMillis, to show that we are actually using Java in this blog-application. If that isn’t apparent from the call to System, remember that we are actually using JDBC to talk to our database, and very soon you will be able to use the ActiveRecord-JDBC adapter to connect to any databases Java can talk too. That’s a brigth future.