Results tagged “ruby” from McWong

How J2EE set architecture back a ways

|
I'll start this off by saying this is pure opinion.  I don't have any statistics.  Instead I have some gut intuition based on numerous "enterprise Java" projects within my company, and through observing the development community.

I'm currently involved in architecting a next generation systems management framework.  It has fairly hefty requirements... monitor and manage millions of objects distributed across the globe in real time.  Take out the "millions" bit, and this isn't so bad.  There are a bunch of standards out there (JMX, SNMP), there are some off the shelf tools, and you can knock something together pretty quickly that covers a reasonable subset of equipment for small installs.  But we need to do better.   We need to manage more than just "most" of the equipment, and we need to do it in some of the world's biggest data centers.  And as I talk about this with my peers from other groups,  I'm getting more convinced that J2EE has poisoned a good chunk of the current generation of architects.

I know this isn't how J2EE  has to work, but in most projects, you start them like this...
1) Build your object model, use magic to map it to the DB
2) Build your business logic
3) Build your front end
4) Ship it!

And there you are.  You have a nice central database so you don't have to worry about distributed data so much.  It's all nice and safe on the disk.  You have some adapters to get stuff in and out, and a cozy UI that serves it all up to the user.  And it scales... for as much load as you can throw at your desktop.

Then you take it to some place big.  And it just flat out doesn't work.  It's not a matter of getting a bigger database, because you just can't scale it up past a certain point. You can cluster, but now you need a professional services group to install your product, and the performance gains to be had aren't crystal clear.

It becomes a matter of deciding what goes in there.  And when, and how.  But looking forward to these issues is met with fierce resistance in design meetings... Building distributed systems is much harder than building something around a central store.  Objections are typically based on reading marketing propaganda from database vendors.  Responses of the form "well, we'll partition the data when we hit that load.  Or use a cluster... yeah, a cluster will fix everything" are extremely painful to work through.  After all, some "expert" from VendorX says it will work. Who are you, my peer, to assume you know more than me?  

But guess what?  Each of these objectors has a product in the field, based a central database, handling far less load than our targets, and none of them scale well.

Scaling out is beating scaling up.  Processors are going multi-core, not taking huge leaps forward in the GHz war.  Scala and Erlang are gaining in popularity, and with them a different model of parallelism (not new, just different) than that offered by J2EE.  The actor model embraced by both of those languages is primarily targeted at small processes within a service.  But as you look at larger chunks of the architecture, I think the sanest way forward is to embrace a similar model.  

ESB has been a buzzword for years now.  I don't buy all the marketing hype around it, but at its core there is a model that the current generation of architects needs to get a handle on.  It's very similar in concept to the Erlang/Scala actor model.  Yes, it's harder to design around asynchronous messages and a large number of distributed components.  But that's how you handle the big problems.  And that's why you hire good developers to work on them, not just some random CS grad who happens to be able to regurgitate the latest Spring book.  

I'm not saying there isn't a place for the traditional J2EE buildout (rails has a similar model, and works wonderfully for small-medium sized projects).  And in fact, even with these asynchronous systems there are almost certainly services that need a DB backing them.  But there are other tools that should be in your toolbelt.

My suggestion to Java architects today would be to pick up another language that fosters a totally different architecture.  I'm currently biased towards Ruby, Erlang or Scala.  There are no guarantees that any of these scale better than Java (and it's fairly easily arguable that Ruby doesn't).  But any of these will make you see the world in a different way, and allow you to more effectively consider and evolve your current architecture.

Scala Lift Off - Static Companion to Ruby?

|
So I went to the last half of Scala Lift Off on Saturday (only half, because the first half was taken up by my final MBA class.  Ever.).  I went primarily out of curiosity, not knowing much about Scala or Lift.  The main draw was the built in comet support for Lift, which seems to not be a focus in other frameworks... at least not for Rails.  We currently use Juggernaut for comet support, but depending on flash is something of a liability (see: iPhone), and Juggernaut itself isn't as smoothly integrated with Rails as i'd like.

I came away extremely impressed.  Scala is relatively unheralded in the world of alternative JVM languages (see Groovy, Jython and JRuby publicity), but shows a lot of promise.  It's a functional language with an expressive syntax that allows you to easily create code that looks DSL-ish.  These are the primary features that drew me to Ruby (ok, Ruby isn't a functional language, but you can sorta fake it).  But Scala has a better integration story with existing Java libraries, is strongly typed, and has a stronger functional bent.

I'm a big believer in the right tool for the job, and as such don't fall into a pure-dynamic or pure-static language camp.  I also don't fall into a single language camp. I really enjoy Ruby for quick prototyping, and love Rails for quick prototyping of webapps, and maintaining a nimble production face on web applications.  But Rails falls down when I need to run background processing.  The times I think hardest about moving back to a Java webapp environment are when I need to go write something that doesn't just receive a web request and terminate.  This is where concurrency issues get painful to deal with, Ruby daemons/DRb are painful, and starting up a whole Rails env for simple processing is rough.

So I'm hoping Scala/Lift fills that void.  I'm mentally sketching out a replacement of our background processing jobs (Twitter integration, email processing, etc.) with Scala, and in particular the Actors library.  These are relatively simple processing tasks, and should give me a decent feel for the language.  It should also improve the stability and scalability of our background processing.  It may also yield some reasonable libraries to contribute back (Scala Twitter library, Scala ActiveRecord bridge). 

Once I have that nailed down, an evaluation of how Lift can/should fit into the framework is in order... or maybe I'll have to start my Rettiwt side project based on Lift.


Transparent PNGs in IE w/ Rails

|
I've been working on Kebima for several months now, using Firefox and Linux/OSX.  Chalk it up to not doing enough research, but I just figured transparent PNGs worked in IE.  Oh well.  They don't.  At least not in 6 and earlier.  So began my mission to get them to work.

I was already using a lightbox package that uses the technique mentioned in the MS support article, but I didn't want to have to apply a div-specific solution for every png on my page... and since i'm using the silk icon set, this would mean a lot a lot of specificity.

Googling gets you a lot of results.  The first hit is actually pretty good, in that it states that the script isn't maintained, but points you to 24 Ways, which has a pretty good solution.  This likely works out of the box for normal web development (I did run into one bug... for some reason the section of the script that sets root on line 17 failed... I took out the bit that allowed you to limit the div the script was applied to).  However rails likes to throw timestamps on the end of images, so instead of '/images/icon.png' you get '/images/icon.png?1209327623'

Because the 24 Ways script is looking for an img tag whose src attribute ends with '.png', this makes things not work.  My solution was to add a regex to match rails style image srcs

    var png_pattern        = /\.png(\?\d*)?$/i;

And then match against that in fnLoadPngs

            // background pngs
            if (obj.currentStyle.backgroundImage.match(png_pattern) !== null) {
                bg_fnFixPng(obj);
            }
            // image elements
            if (obj.tagName=='IMG' && obj.src.match(png_pattern) !== null){
                el_fnFixPng(obj);
            }

And it worked.  Plus I got a pretty decent workout running up and down the stairs between Mac and PC.

Standalone ActiveRecord and SQLite3

|
I recently wanted to set up a quick daemon process that used ActiveRecord outside the rails framework. I also wanted it to use sqlite, just to keep install/dependencies simple. I found not so good documentation on how to do this... and after a few missteps it turns out it's not that hard. The only gem dependencies are activerecord and sqlite3-ruby. You'll also need sqlite working. Code follows
require 'rubygems'
require 'sqlite3'
require 'activerecord'

# connect to database.  This will create one if it doesn't exist
MY_DB_NAME = ".my.db"
MY_DB = SQLite3::Database.new(MY_DB_NAME)

# get active record set up
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => MY_DB_NAME)

# create your AR class
class Update < ActiveRecord::Base

end

# do a quick pseudo migration.  This should only get executed on the first run
if !Update.table_exists?
  ActiveRecord::Base.connection.create_table(:updates) do |t|
    t.column :account_name, :string
    t.column :last_update_time, :timestamp
    t.column :last_update_id, :integer
  end
end

Scaling and Deploying Rails: It's not Java. Or PHP.

|
Recently there have been a slew of posts complaining that rails doesn't scale and is hard to deploy.  I think the root of this is that people come to rails after experience with Java, PHP or similar frameworks.   Rails is neither of these.  It is heavier than PHP, and it seems that this makes its fit with the typical PHP deployment methods slow at best.  At the same time it has an entirely different deployment model than Java web applications.  The Java application containers are heavier, and designed to support a much more highly available application within  that JVM instance.

But rails still has its sweet spot.  So why would I use rails instead of Java? It's not as light as PHP, but from a development perspective it's usually close enough.  Bouncing webrick or mongrel takes a second or two, and for most changes I don't even need to do this.  To accomplish the same code/test cycle in Java takes  at least 10 times as long, often much worse.  At the same time rails offers.  Plus I can do a lot of things I can't do with Java, thanks to dynamic typing, better metaprogramming facilities, and things like the rails console.  Plus, from a hosting perspective you can get much better deals on hosting a rails application than a full Java servlet environment, which tells you something about the relative difficulty of hosting rails and J2EE.

Why would I use it instead of PHP?  The framework does enough magic that getting an application up and running is faster than raw PHP (disclaimer:  I haven't used any of the newer PHP frameworks like CakePHP . Maybe they're more on par with rails here.).  MVC is nicely broken out.  Ruby itself is a wonderful language to develop in.  Yes, you're going to have to do more work around deployment.  I can't imagine going live using FastCGI, SCGI, or that sort of thing.  But learn how to set up a mongrel cluster.  That seems to work.

I think Java and PHP still have their places as well.  Java provides much better concurrency in a single process, better built-in capabilities for high availability, security, and a larger library/tool ecosystem.  Enterprises know how to deploy and maintain Java applications, and in general feel more comfortable putting those in production than a rails system.  PHP is lighter weight, will likely scale better out of the box, and is better understood by most hosting companies.

At the end of the day the decision comes down to what you're building and where you're deploying.  I think rails works in wide range of situations, but there is an equally wide range where PHP or Java may be a better choice. 
I'm in the process of building a REST service using rails and ActiveResource. On the server side everything was fairly straightforward, but I hit a snag when I moved to the web tier. For various reasons I wanted to have two separate servers, one providing the REST service and the other serving HTML content. The rub is that ActiveResource assumes that credentials are a class level attribute. I'd like the calls to the ActiveResource client to use the credentials of the currently logged on user, but there doesn't appear to be a straightforward way to do this out of the box. Metaprogramming to the rescue!

Using the REST example from my last post, I extended the client in the following way.

require 'activeresource'

module Sample
  module Client
    class API
      #
      # Creates a module that serves as an ActiveResource
      # client for the specified user
      #
      def self.create_api(login = nil, password = nil)
        # specify the site.  Default to no credentials
        @url_base = "http://localhost:3000"
        @url_base = "http://#{login}:#{password}@localhost:3000" if login
        # build a module name.  This assumes that logins are unique.
        # it also assumes they're valid ruby module names when capitalized
        @module = login ? login.capitalize : "Default"
        class_eval <<-"end_eval",__FILE__, __LINE__
        module #{@module}
          class Post < ActiveResource::Base
            self.site = "#{@url_base}"
          end
          class Comment < ActiveResource::Base
            self.site = "#{@url_base}/posts/:post_id"
          end
          # return the module, not the last site String
          self
        end
        end_eval
      end
    end
  end
end

This new version allows you to create an api for a specific user. You can squirrel this away and use it for specific users, allowing you to use multiple connections to your rest service. A quick irb session follows

>> require 'ares_sample_client'
=> ["Sample"]
>> api = Sample::Client::API.create_api
=> Sample::Client::API::Default
>> p = api::Post.find(1)
=> #<Sample::Client::API::Default::Post:0xb715af74 @attributes={"updated_at"=>Wed Jan 09 02:36:34 UTC 2008, "id"=>1, "content"=>"The first post", "user_id"=>1, "created_at"=>Wed Jan 09 02:36:34 UTC 2008}, @prefix_options={}>
>> p = api::Post.create(:content => "should fail")
ActiveResource::UnauthorizedAccess: Failed with 401 Unauthorized
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:125:in `handle_response'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:112:in `request'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:101:in `post'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:803:in `create'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:636:in `save_without_validation'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/validations.rb:262:in `save'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:339:in `create'
        from /usr/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/core_ext/object/misc.rb:28:in `returning'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:339:in `create'
        from (irb):4
>> auth_api = Sample::Client::API.create_api('test1','test1')
=> Sample::Client::API::Test1
>> p = auth_api::Post.find(1)
=> #<Sample::Client::API::Default::Post:0xb713dde8 @attributes={"updated_at"=>Wed Jan 09 02:36:34 UTC 2008, "id"=>1, "content"=>"The first post", "user_id"=>1, "created_at"=>Wed Jan 09 02:36:34 UTC 2008}, @prefix_options={}>
>> p = auth_api::Post.create(:content => "should succeed!")
=> #<Sample::Client::API::Test1::Post:0xb713312c @attributes={"updated_at"=>Thu Jan 10 04:01:53 UTC 2008, "id"=>7, "content"=>"should succeed!", "user_id"=>nil, "created_at"=>Thu Jan 10 04:01:53 UTC 2008}, @prefix_options={}>
>>

Quick REST with ActiveResource and rails

|
   
Posted by McWong 53 days ago
This walkthrough shows the creation of a simple REST API using ActiveResource. It includes a nested model, authorization scheme, and a client library. The intent is not to exhaustively explore ActiveResource, but to show how to quickly get up and running with a reasonable REST API.

Step 1: create an application

rails ares-demo

I'm using rails 2.0.2 here, but I don't think anything is version specific.

Step 2: install two great rest plugins
Resource This is a plugin that adds default rest-based actions to a controller. It has so-so support for nested resources... you won't be able to use it out of the box for HABTM relationships for instance, but for strict hierarchies it works well, and really beats hand coding the controllers. You can install it with

script/plugin install http://jnewland.com/svn/public/ruby/rails/plugins/resource_this/

Restful Authentication is derived from acts_as_authenticated, and adds a rest-ish session controller, as well as the usual controller helpers (login_required, etc.) You can install it with

script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication/

Step 3: setup restful authentication
This bit is easy. Just run the generator to get your migrations for users created, along with a couple other things. I honestly didn't dig into this too much, it just works.

./script/generate authenticated user sessions

Step 4: create application model
Next up is generating your model. We'll do a simple one. Posts contain many Comments. Both Posts and Comments belong to a user.

./script/generate scaffold post
./script/generate scaffold comment

Next set up your migrations. db/migrate/002_create_posts.rb should contain

class CreatePosts < ActiveRecord::Migration
  def self.up
    create_table :posts do |t|
      t.column :content, :text
      t.column :user_id, :integer
      t.timestamps
    end
  end

  def self.down
    drop_table :posts
  end
end

and db/migrate/003_create_comments.rb should contain

class CreateComments < ActiveRecord::Migration
  def self.up
    create_table :comments do |t|
      t.column :content, :text
      t.column :user_id, :integer
      t.column :post_id, :integer
      t.timestamps
    end
  end

  def self.down
    drop_table :comments
  end
end

Next you'll need to add the relationships to your model classes. Add the following bit to the front of app/models/user.rb

class User < ActiveRecord::Base
  # custom relationships
  has_many :posts
  has_many :comments

app/models/post.rb should look like

class Post < ActiveRecord::Base
  belongs_to :user
  has_many :comments
end

and app/models/comment.rb should look like

class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :post
end

Now run migrate to get your database all configured

rake db:migrate

Step 5: populate dummy data
Next we'll want to get some default data in there. Execute script/console and run through the following

>> u1 = User.new(:login => "test1", :email => "test1@foo.com", :password => "test1", :password_confirmation => "test1")
=> #<User id: nil, login: "test1", email: "test1@foo.com", crypted_password: nil, salt: nil, created_at: nil, updated_at: nil, remember_token: nil, remember_token_expires_at: nil>
>> u1.save
=> true
>> u2 = User.new(:login => "test2", :email => "test2@foo.com", :password => "test2", :password_confirmation => "test2")
=> #<User id: nil, login: "test2", email: "test2@foo.com", crypted_password: nil, salt: nil, created_at: nil, updated_at: nil, remember_token: nil, remember_token_expires_at: nil>
>> u2.save
=> true
>> p = Post.new(:user => u1, :content => "The first post")
=> #<Post id: nil, content: "The first post", user_id: 1, created_at: nil, updated_at: nil>
>> p.save
=> true
>> c = Comment.new(:user => u2, :post => p, :content => "first!")
=> #<Comment id: nil, content: "first!", user_id: 2, post_id: 1, created_at: nil, updated_at: nil>
>> c.save
=> true
>>

Step 6: resource_this!
Now the boring part us over. So far we have a basic rails app. Now we get to the REST part! You're three edits away... Change app/controllers/posts_controller.rb to the following

class PostsController < ApplicationController
  resource_this
end

Change app/controllers/comments_controller.rb to the following

class CommentsController < ApplicationController
  resource_this :nested => [:post]
end

And finally update config/routes.rb

ActionController::Routing::Routes.draw do |map|
  map.resources :posts do |post|
    post.resources :comments
  end

  map.resources :users

  map.resource :session
end

That's it! You're ready to look through your API

Step 7: browse
Start up your server with script/server. You'll need to clean up the erb views if you want to use the HTML interface, but your xml interface should be working just fine. Try the following urls for verification

    * http://localhost:3000/posts/1.xml
    * http://localhost:3000/posts/1/comments.xml

Step 8: lock it down
So far our service is open to anybody. We'll add very simple authentication at this stage... we'll require a valid user for anything other than show and index requests. All we have to do for this is include AuthenticatedSystem in our application controller, and add a before_filter. Make app/controllers/application.rb look like the following

class ApplicationController < ActionController::Base
  include AuthenticatedSystem
  before_filter :login_required, :only => [:create,:edit,:new,:update,:destroy]
  helper :all # include all helpers, all the time

  # See ActionController::RequestForgeryProtection for details
  # Uncomment the :secret if you're not using the cookie session store
  protect_from_forgery # :secret => 'b845cf981afbfef06600e5d7f23e0fed'
end

Step 9: create client lib
We could test this with curl or a browser, but the real fun of ActiveResource is getting to use ruby objects. So... we'll create a simple client api. Create a new file in lib called 'ares_sample_client.rb' and add the following code

require 'activeresource'

module Sample
  module Client
    class API
      class Post < ActiveResource::Base
        self.site = "http://localhost:3000"
      end
      
      class Comment < ActiveResource::Base
        self.site = "http://localhost:3000"
      end
    end
  end
end

Now we can play with our creation using the console. Make sure your server is running on port 3000, and startup script/console.

         
>> require 'ares_sample_client.rb'
=> ["Sample"]
>> posts = Sample::Client::API::Post.find(:all)
=> [#<Sample::Client::API::Post:0xb710bec4 @attributes={"updated_at"=>Wed Jan 09 02:36:34 UTC 2008, "id"=>1, "content"=>"The first post", "user_id"=>1, "created_at"=>Wed Jan 09 02:36:34 UTC 2008}, @prefix_options={}>]
>>

>> p = Sample::Client::API::Post.create
ActiveResource::UnauthorizedAccess: Failed with 401 Unauthorized
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:125:in `handle_response'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:112:in `request'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:101:in `post'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:803:in `create'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:636:in `save_without_validation'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/validations.rb:262:in `save'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:339:in `create'
        from /usr/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/core_ext/object/misc.rb:28:in `returning'
        from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:339:in `create'
        from (irb):3
>>

Our API works, and the authentication is as it should be. We didn't specify credentials, so we can't actually create a post. Let's fix up our client lib to add credentials

module Sample
  module Client
    class API
      class Post < ActiveResource::Base
        self.site = "http://test1:test1@localhost:3000"
      end
     
      class Comment < ActiveResource::Base
        self.site = "http://test1:test1@localhost:3000"
      end
    end
  end
end

Note that credentials apply to the whole class... this makes the API fairly easy to use, but makes using multiple users difficult. Anyway, with our authentication in place, let's try again

>> p = Sample::Client::API::Post.create(:content => "Should succeed", :user_id => 1)
=> #<Sample::Client::API::Post:0xb71b81d8 @attributes={"updated_at"=>Wed Jan 09 02:55:22 UTC 2008, "id"=>3, "content"=>"Should succeed", "user_id"=>1, "created_at"=>Wed Jan 09 02:55:22 UTC 2008}, @prefix_options={}>
>> p.save
=> true
>>

Sweet! Now we can not only browse posts and comments, but create them as well!