Archive for Rails

RubyAMF 1.3.2a

It’s that time again. Here is RubyAMF 1.3.2a. This has a number of really good fixes in it.

First I’ve added support for BigDecimal. What does that include? Native NaN, Infinity, -Infinity de/serialization. Now those values will be handled by RubyAMF. There is also top level functions isNaN and isFinite to support those new values.

Here is a list of the new pieces that go into BigDecimal support on the Ruby side:

isNaN / isNaN?
isFinite / isFinite?
NaN       #not a number.
Infinity   #infinity (Infinity / Math.POSITIVE_INFINITY)
NInfinity #negative infinity (-Infinity / Math.NEGATIVE_INFINITY)

As an example, immagine we do this as a service call from Flex:

myRO.getNaN()

And in Ruby:

def getNaN
  #LITE:
  return NaN
  #RAILS:
  render :amf => NaN
end
def getInfinity
  Infinity
end
def getNinfinity
  NInfinity
end

The result will be NaN in the player. Same goes for Infinity / NInfinity. Note that AMF0 only supports NaN.

I’ve also cleared up an issue with outgoing Date objects having problems being cast in the Flash player. The problems don’t happen anymore, but the solution was to skip writing any references to other Date objects in the AMF stream.

Previously, the player would get hung up on referenced Date objects while deserializing the AMF – which caused the player to throw a TypeError. The strange thing is that even though there was a casting error, the dates were still usable and correct.

Now I just force it to write every Date object to the stream, which fixes this. But is this a problem with the player?

See the release log for more info on this release.

1.3.2a is now in the Rails installer as well.

More Updates Coming

I’ve added better support for params hash mapping. So those that tried to do the following but ended up failing, can now do it.

def update
 u = User.find(params[:id])
 u.update_attributes(params[:user])
 if u.save
   respond_to do |format|
     format.amf { render :amf => true }
   end
 end
end

That wouldn’t work previously. So I have an update for this in /trunk that now handles this.

Actually, here’s a good time to explain how remoting parameters map to the params hash in rails.

-All incoming VO's get put into params[:CLASSNAME]. EX(params[:user]).

-If an Incoming VO is not an active_record, but a custom vo which maps
 to a fully qualified class path (:map_to => 'my.package.class.MyCustomVo').
 It will be put in params[:mycustomvo]

-All incoming parameters ALSO get put into their index. EX(user = params[0]).

-For incoming active_record VO's, the params[index] is an update hash.

-If the first param is an Object or VO, it's property values are merged
 into params. (params[:id], params[:firstname], etc..)

Make note that the only time an objects properties are merged into the params hash is if it’s the FIRST parameter in the remoting call. As usual – if anyone has an idea to better this just let me know.

These updates are in /trunk for now, and I will release 1.3.2 as soon as I get BigDecimal support in.

RubyAMF 1.3.1

1.3.1 adds deep result adapters. You have to opt in to this (in the adapters_config) file. But this means that even if you send results wrapped in array’s it will correctly send back what you’re expecting.

So if you do this:

def my_method
  u = User.find(:all)
  p = People.find(:all)
  a = Array.new(u,p)
  render :amf => a
end

The results in Flex / Flash will be an array with two elements ([0],[1]). Which contains another array of objects. Just like you would expect.

As mentioned, you have to turn this on specifically in the adapters_config file. As this has the potential to slow everything down. So it’s off by default. This is the same for both RAMFL and RAMFR.

1.3.1 is now in the Rails installer as well.

ruby script/plugin install svn://rubyforge.org/var/svn/rubyamf/tags/current/rubyamf

Rails Controllers and rescue_action

Here’s a random but useful note. If you make a remoting request to a controller, but an exception happens and you want to be able to pipe it back to flash. Use rescue_action in whatever controller you’re calling.

Here’s an example:

require RUBYAMF_HELPERS + 'fault_object'
class UsersController < ApplicationController
 def rescue_action(e)
  respond_to do |format|
   format.amf { render :amf => FaultObject.new(e.type, e.message) }
  end
 end

 def list
   raise ArgumentError
 end
end

In this example, making a call to the list method simply raises an ArgumentError for an example, but ends up being sent to the rescue_action method, which in turn renders AMF for you.

Then in Flex / Flash, you can inspect these fault object properties to see some more detail:

private function onFault(fe:FaultEvent):void
{
  trace(fe.fault.FaultString);
  trace(fe.fault.FaultCode);
  trace(fe.fault.faultDetail);
  trace(fe.fault.rootCause);
  trace(fe.fault.extendedData);
}

Sweet!

See api.rubyonrails.org -> ActionController::Rescue for more information.

Remoting Call Parameters and Mapping them to Rails’ Params

I put a quick update into RC2 (in svn) that puts Value Objects in the params[] hash by their instance type. So if you send a Person VO from Flex, it will get put in params[:person]. I have a slight dilemma and I wanted to hear from everyone who has been using RubyAMF to see what they might like better.

The dilemma is when you send a VO from Flex to RAMF, it automagically gets turned into an ActiveRecord instance. In the above example. params[:person] is already an instance of an ActiveRecord. And here comes the dilema. Some people want to be able to call p = Person.new(params[:person]). But because it’s already an ActiveRecord instance an exception is raised.

So what would you rather have? params[:person] == ActiveRecord, or params[:person] == update hash. Or maybe you would rather let the ActiveRecord instance be in params[0], and the key/val be the update hash? Or would you want to be able to toggle how RubyAMF handles that kind of parameters mapping?

I’d like to hear from some others, I don’t want to do what I like when the majority possibly likes it another way.

1.3 RC2. Get it While it’s HOT!

Finally – 1.3 RC2. This release takes the Rails plugin one step further and gives you full Controller functionality. Now anything you do in a controller, will be ok. Pagination, authentication plugins, scaffolding, all action filters, etc.

Most importantly. RAMFR does not harm Rails’ Production environment anymore. After writing about 5 different ways of doing this Rails plugin I finally got it. It was dead simple. Not only was it simple, but this is how I got full controller support.

Previous problems with the RAMFR plugin and Rails’ production environment caused another infamous “argument errors (0 for 1).” This was because in the RAMFR Rails plugin I re-defined the “render” method so that “render :amf” would work. But that would mess with your production environments because the Rails framework isn’t reloaded in production. So anyway, with RC2 it is fixed.

Another very important change is that you *MUST* use “render :amf” to respond to Flash correctly. No more having to use “return something.” You can use “render :amf => content” in the method anywhere, or in the “respond_to” REST style. So if you do a remoting call to the controller and you get “null” back as a response, make sure your using “render :amf”.

Here are some examples of the different ways you now respond to RubyAMF.

#normal
def getNewUser
  u = User.new
  u.firstname = 'aaron'
  u.as_single!
  render :amf => u
end

##shortcut for above
def getNewUser
  u = User.new
  u.firstname = 'aaron'
  render :amf => u.as_single!
end

#rest respond_to style
def getNewUser
  u = User.new
  u.firstname = 'simeon'
  respond_to do |format|
    format.amf { render :amf => u.as_single! }
  end
end

#scaffold, example, just add respond_to
def list
  @user_pages, @users = paginate :users, :per_page => 10, :include => :addresses
  respond_to do |format|
    format.amf { render :amf => @users }
  end
end

#@is_amf / @is_rubyamf are still available for logical reasons
def getUser
  if @is_amf
    #something
  else
    #something
  end
  respond_to do |format|
    format.amf { render :amf => #something }
  end
end

Lastly, the one thing that is still kind of annoying is how you get parameters that were passed from Flex/Flash. If you’re sending two parameters. They come in as params[0], and params[1]. The whole reason that this happens is that your parameters from Flex / Flash aren’t given keys to be accessed by. So what can we do?

A possible feature for 1.3 Final will be parameter mappings. So in order to get some parameters by key – like :id, :name, :user, etc.. You could define a ParametersMapping like so:

ParameterMappings.register('org.unversalremoting.browser.AMFTests.getString',:id => 0, :name => 1)

This will in turn populate your params hash with the corresponding values from Flex/Flash. This takes us one step farther towards not having to do a darn thing in controllers except decide when we want to send back AMF from the controller.

I’m still kind of throwing this idea around. Does it seem like too much work just to access the parameters by key? Let me know what you think.

Also, I haven’t been able to get anywhere quiet to do some screencasts. But I will be able to get some done this weekend. Hold on to those shorts.

Rails Production Environment

I just fixed the biggest issue that was left for RubyAMF Rails (being production environment safe). I’ll be putting out 1.3RC2 in a couple days. Along with some more screencasts. The screencasts that are up now don’t really do justice for 1.3.

RubyAMF 1.3 RC1

RubyAMF 1.3 is almost ready and includes many new features and improvements. Here is the first of two release candidates. I wanted to get this out because there are a lot of improvements and additions.



First let’s talk about what’s changing for both the Rails and Standalone version. And what’s include in RC1.

Name Changes
Up until now I’d been referring to RubyAMF as “standalone” and “on rails”, I’m going to start calling them “RubyAMF Lite” and “RubyAMF Rails”. Lite being the built in application server. RubyAMF Rails of coarse means running in Rails. For short I’ll be refering to them as “RAMFL” and “RAMFR.”

Bug Fixes
There were three critical bugs found in 1.2 that probably caused a lot of headache. And possible “screw it” scenarios. Two of these are fixed in RC1.

The most notable was the “nil.unpack in read_word8″ error. When using the flash debug player, about 50% of the time you would encounter that error. The other 50% it was fine. This was caused by Rails’ ActionPack / ActionController gem.

Rails’ ActionController has a line that fixes AJAX requests from Safari. Safari appends a \000 to the end of the binary stream, which Rails doesn’t like.

Here’s the exact line from actionpack1.13.3/lib/action_controller/cgi_ext/raw_post_data_fix.rb

# Fix for Safari Ajax postings that always append \000
content.chop! if content[-1] == 0

So ActionController chop’s that last byte off – IF the last byte in the stream is \000. BUT AMF also has an \000 as the last byte. So as mentioned, half of the time ActionController was chopping off the last byte, thus causing the RubyAMF deserializer to choke on it. I’ve put a bug fix in the deserializer to handle this case.

RubyAMF configuration file
I moved value object definitions and adapter definitions into a new configuration file.

RAMFR, the config file is in rails/config/rubyamf_config.rb.

RAMFL, the config file is in services/rubyamf_config.rb

Value Objects
1.3 brings completed value object functionality and ActiveRecord value objects. What does this mean? Now you can pass ActiveRecords back and forth with no further work on your part.

Here is an example value object definition from the config file:

ValueObjects.register({:incoming => 'Person', :map_to => 'Person', :o utgoing => 'Person' [, :type => 'active_record', :instance => 'universalremoting'] })

In any VO definition, :type, and :instance are optional. When you supply the :type of active_record, RAMFR knows to turn an incoming Person object into a Person active record. Perfect.

Also, in this example you notice the :instance property. This is a new feature for RAMFL and is never needed for RAMFR. Keep on reading. The section about Application Instances is what this applies to.

ActiveRecord#as_single!
If you want to return a new ActiveRecord, and don’t want it to be returned in an array (for a dataProvider), you use the new ActiveRecord::Base#as_single! method. This tells RubyAMF to write the result as an object, not an object in an array ({}, not [{}]). This is something that needs to be specified intentionally.

The reason why this needs to be called intentionally VS me catching a single in RubyAMF and sending it back as just the object is this – say you have a service that returns User.find(:all). What if you only have one record? If I sent it back as just the object, chances are your onResult would throw an exception because you were expecting an Array.

By making you intentionally call it, that must mean you know your onResult method is expecting an Object or VO. Sweet!

Here’s a quick example:

def newUser
  u = User.new
  u.name = 'dave'
  u.phone = '234234234'
  u.save
  return u.as_single!
end

This example returns a User value object back to your application. So you would access the name property like so:

function onResult(event:ResultEvent):void
{
   var u:User = event.result as User;
   trace(u.name);
}

If you don’t use “as_single!”, the result would be access like this:

function onResult(event:ResultEvent):void
{
  var u:User = event.result[0] as User;
  trace(u.name);
}

Recursive ActiveRecord Adapter
The ActiveRecord adapter is now recursive to include associated model data. Heres’s an example.

User.find(:all, :include => :addresses)

In the resulting array, each item in the array has an “addresses” property that is another array of either Objects or ActiveRecord value objects, depending on whether or not you’ve defined a VO mapping for Address.



Whew that was a lot! Everything I just went over is available in both RAMFL and RAMFR.

Now let’s talk about what’s new for Lite.

Application Instances
Application Instances define a scope that allows RubyAMF to initialize ActiveRecord and load models for a request. They also create a scope for value object definitions. If you have multiple applications running in RubyAMF Lite with the same model names, you can declare a value object to part of an application instance so that it will always use the right model.

For every request, RubyAMF Lite tries to match the target path (org.package.SomeService) against an Application Instance definition. If a matching definition is found for that request, ActiveRecord is initialized, models are loaded and the database is connected to; based on the parameters you specify in the definition.

Here’s an example app instance definition from a config file.

Application::Instance.register({
  :name => 'universalremoting',
  :initialize => 'active_record',
  :source => 'org.universalremoting.browser.*',
  :database_config => 'org/universalremoting/browser/test.yaml',
  :database_node => 'development',
  :models_path => 'org/universalremoting/browser/support/ar_models/*'
})

Application instance definitions are not required for RubyAMF Lite to function properly. App instances main purpose is for incoming ActiveRecord value objects. Because ActiveRecord must be connected before instantiating an AR instance – app instances allow RubyAMF to catch requests and do the necessary AR connecting before you receive anything in your service method. So if you’re expecting ActiveRecord value objects you MUST define an application instance.

These definitions also make your service “model” enabled. By that I mean you don’t have to require rubygems, or any of your model files. You can just use them. Because they’re loaded when the application instance is initialized.

If you’re not using ActiveRecord value objects, no application instances are necessary, and RubyAMF Lite will function as normal.




Other Changes

Rails Installer
The Rails installer now puts a rubyamf_config.rb file in rails/config. This is where ValueObjects, and Adapters are defined.

The Rails installer now has 1.3RC1 in it.

ruby script/plugin install svn://rubyforge.org/var/svn/rubyamf/tags/current/rubyamf




So what’s next?

1.3 RC2 is next. In 1.3 RC2. I will fix the last outstanding major issue (bug number three of the three major bugs in 1.2). This issue is with RubyAMF Rails specifically. It was not Production environment friendly. No excuses – I put the Rails plugin together pretty quickly and a I made a slight oversight in the design. Yay! Anyway, it will be fixed an RubyAMF will be fine in production environment.

The bug is that I re-define the ‘render’ method of ActionController::Base. In development environment this is fine. But in Production mode, the Rails framework is not re-loaded for every request, thus once a single RubyAMF request was made, you no longer have render :[format]. Except of course :amf. But that’s probably not going to work for everything.

After RC2, the official 1.3 Final will be released. After that who knows. RubyAMF is scheduled to be in one book for sure. Plus I’ve dabbled in a Ruby FMS RTMP implementation. And I still need to write that damn C extension. I’m really looking forward to the future of RubyAMF. It can only get better.

Also, I’m moving to San Francisco. I’d like to start some RubyAMF sessions so I can meet people who are trying RubyAMF, and answer questions for anyone.

Also, for those who may have remembered talks of the Universal Remoting Group, I’m also sitting on a Universal Remoting Service Browser that enables custom AMF Format de/serialization tests so that any remoting implementation can automate AMF de/serialization correctness. Code generation, service browser, result inspection, recordset rendering, call histories, authentication, retry’s, etc etc. Stick around for that.

Also, please be patient while I update the entire wiki for 1.3. Just email me if anyone has any questions.

-Aaron

Rails Plugin Installer

I finally had some time to re-organize the SVN repo and create a rails installer. Here’s the install command / path:

ruby script/plugin install svn://rubyforge.org/var/svn/rubyamf/tags/current/rubyamf

The new repo is laid out with the suggested standard directories.

/trunk -> development
/tags -> previous revisions. Always defined like: rubyamf_{version}_{revision}
/tags/current/rubyamf -> rails installer directory

Also; thanks to Simeon Bateman for a quick tip on re-arranging the SVN for some better organization, tags for previous version, and the rails installer.

RubyAMF-1.2

RubyAMF-1.2 folks.

With so many requests for respond_to/format.amf functionality, I’m supporting it. Here is an example of using it in a class:

def MyController < ActionController::Base
  def list
    respond_to do |format|
     format.amf { render :amf => User.find(:all) }
    end
  end
end

You also need to declare a new mime type in the rails/config/envirionment.rb file (towards the end of the file). Like so:

Mime::Type.register "application/x-amf", :amf

Download and Enjoy!

« Previous entries · Next entries »