Starter Step

Archive for the ‘Ruby’ Category

This morning I ran into a small roadblock working with the acts_as_state_machine gem. My code started like this:

class Communication < ActiveRecord::Base
  include AASM
  aasm_column :status

  aasm_state :draft
  aasm_state :pending
  aasm_state :approved
  aasm_state :rejected

  aasm_event :submit do
    transitions :to => :pending, :from => :draft
  end

  aasm_event :approve do
    transitions :to => :approved, :from => [:pending, :rejected]
  end

  aasm_event :reject do
    transitions :to => :rejected, :from => [:pending, :approved], :guard => :validate_rejection_reason
  end

  aasm_initial_state :draft

  def validate_rejection_reason
    if self.rejection_reason
      true
    else
      raise AASM::InvalidTransition.new('A rejection reason is required')
    end
  end
end

This is a basic example using AASM to provide an approval process for my Communication model. I wanted the ability to just call instance.reject(“some reason”) or instance.reject!(“some reason”). I was really thinking to hard about it. The assm_event just creates some methods for me….so why not just decorate those methods using ruby aliases. This is what I ended up with:

  include AASM
  aasm_column :status

  aasm_state :draft
  aasm_state :pending
  aasm_state :approved
  aasm_state :rejected

  aasm_event :submit do
    transitions :to => :pending, :from => :draft
  end

  aasm_event :approve do
    transitions :to => :approved, :from => [:pending, :rejected]
  end

  aasm_event :reject do
    transitions :to => :rejected, :from => [:pending, :approved], :guard => :validate_rejection_reason
  end
  alias old_reject reject
  alias old_reject! reject!

  aasm_initial_state :draft

  def reject(reason)
    self.rejection_reason = reason
    self.old_reject
  end

  def reject!(reason)
    self.rejection_reason = reason
    self.old_reject!
  end

  def validate_rejection_reason
    if self.rejection_reason
      true
    else
      raise AASM::InvalidTransition.new('A rejection reason is required')
    end
  end

Notice the use of alias for reject and reject!. Now you have to call reject with a rejection reason. There may be another way to get parameters in to the event call….but this is how I rolled mine ūüôā

Advertisements

I wrote this as a plugin….but it really has nothing to do with rails. I guess it could be packaged as a gem, but its so small that it probably doesn’t make sense. You ever create class variables or class instance variables and then create getters for them for use as statics?

Old Way:

class MyClass
  @items = [1,2,3]

  def self.items
    @items
  end
end

MyClass.items  #[1,2,3]

New Way:

class MyClass
  has_class_attr :items, :data => [1,2,3]
end

MyClass.items  #[1,2,3]

Install:

  .script/plugin install git://github.com/angelo0000/has_class_attr.git

Building on my previous post I created the has_duration_field plugin. As described, I wanted to represent a duration of time on a field in my model. Using the proxy_field plugin, I built the DurationField class and added it as the proxy for my columns tblk and tduty.

Before the has_duration_field plugin:

class Block < ActiveRecord::Base
  proxy_field [:tduty, :tblk], :as => DurationField
end

I just implemented my DurationField class and threw it in my lib dir. As I did that I realized that really using proxy_field and the implementation of DurationField would make a neat little plugin.

With the plugin you can do this:

class Block < ActiveRecord::Base
  has_duration_field [:tduty, :tblk]
end

The plugin just provides an implementation of the DurationField class for you and an easy way to make your column proxy to a DurationField.

Install:

./script/plugin install git://github.com/angelo0000/has_duration_field.git

I spent some time this week and wrote some small plugins. I’ll do a small write for each to explain the purpose and usage. The first plugin is the proxy_field plugin. For Bidbuddy (personal project) I only have about 10 models, but for each model there are a ton of fields that each represent a time duration. For example a field may represent how long a specific aircraft is used for a given schedule. The time is a duration so I have to store the value in seconds in the database. What I found was that I had helpers that would convert those column values in to hours, minutes, days, etc. While this works, it doesn’t feel right. I would rather just get the value of the column and call to_hours on it. I could open the Integer class and add my to_hours method to it, but I would like something a bit more generic that could be reused for other situations. What I came up with was this syntax:

class Block < ActiveRecord::Base
  proxy_field [:tduty, :tblk], :as => DurationField
end

class DurationField
  def initialize(seconds)
    @seconds = seconds
  end

  def to_hours
    @seconds / 60 / 60
  end

  #Other useful methods would go here
  #to_seconds, to_minutes, to_days, to_weeks, etc...
end

This allows me to proxy any ActiveRecord field into another object. It basically allows you to deserialize any column data.

Old Way:

  def seconds_to_hours(seconds)
    seconds / 60 / 60
  end

  b = Block.find(CONDITIONS_HERE)
  puts seconds_to_hours(b.tduty)

New Way:

  b = Block.find(CONDITIONS_HERE)
  puts b.tduty.to_hours

Install:

  ./script/plugin install git://github.com/angelo0000/proxy_field.git

I have not decided yet how to handle nil columns. Its not a technical challenge but more of a design decision. The plugin could return nil when you called the method to get the proxy: b.tduty would return nil. Or that could be up to the proxy object class to return nil for each method if the inializer got a nil. In my duration example to_hours method would nil if @seconds was nil. I’m not sure what I would prefer. I would love a suggestion…

Recently, I needed to create a background process to offload a long export process. The easiest approach was to just utilize a rake task that would be kicked off by the system command. I ran into an issue because I needed to capture the rendering of a view to create the export file. This is an example of the code that came out of the exploration:

  task :run_export => :environment do
    av = ActionView::Base.new(Rails::Configuration.new.view_path)
    av.class_eval do
      include ApplicationHelper
    end
 
    chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
    file_name = "export_"
    10.times { file_name << chars&#91;rand(chars.size-1)&#93; }

    data = av.render(:partial => "shared/export", :locals => {:surveys => Survey.all})
    FileUtils.mkdir_p "#{RAILS_ROOT}/public/system/exports}"
    File.open("#{RAILS_ROOT}/public/system/exports/#{file_name}.xls", 'w') {|f| f.write(data) }
  end

The above rake task will create an instance of ActionView so that we can call render. My partial also needed access to some helpers I have defined in my ApplicationHelper, so it gets included. Next we generate a psuedo random file name for our export. Then we create a directory for our export files. The directory and its parents are only created if they do not exist. Lastly, we write the result of our render to our file.

Not sure if this is the best approach to this..but seems to work for me.

I have been working on a port of the popular PureMVC framework to my beloved language Ruby.¬† Brian introduced me to PureMVC with our latest Travel Studios project “tripview”.¬† We were finding that our Objective-C codebase was becoming unmanageable and we had horrible coupling between our view components.¬† Brian’s thought was to bring the power of MVC to the Objective-C world for iPhone Development.¬† The result was a new port of PureMVC with Objective-C.¬† We have been using it for a month or so and I had really let us compartmentalize our code as we would in our services layer.¬† We have even introduced some cool dynamic features on top of the PureMVC core to help create some “Black Magic” for us.

Objective-C is definitely not Ruby..and being the Ruby fan that I am, I wanted to take the same great idea and try it in my favorite language.  The result is the PureMVC Ruby port.  I used wxRuby to implement the canned Employee Admin application that Cliff Hall asks all owners of ports to complete as a pre-requisite to releasing the port.  You can read more about the release at: http://puremvc.org/content/view/122/1/



  • Dave: I can tell you're a ruby guy because you forgot the 'return' keyword. Thanks for the tip though!
  • Chandrashekhar H M: Hi, Thanks its working fine in iOS 6 but not in iOS 7.0. Any Suggestion on this.
  • Coeur: To change a rootViewController, without all this TVNavigationController : myNewRoot = [[UIViewController alloc] init]; myNavigationController.view