Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

State dependent methods #169

Closed
willcosgrove opened this Issue Mar 28, 2012 · 6 comments

Comments

Projects
None yet
3 participants

Hey, I'm having an issue getting method definitions working with state_machine. The class I'm building is a Measurement class which should have methods #to_ft, #to_mm, #to_cm, and #to_in. A Measurement has two instance variables, @value and @unit. The @unit instance variable is supposed to be the state, and depending on what it's state is, the methods listed above should change what their approach is to convert the value. Here's my code:

require 'state_machine'

module PrintCalculator
  class Measurement

    attr_accessor :value, :unit

    state_machine :unit, :initial => :in do

      state :in do
        def to_cm
          PrintCalculator::Measurement.new(@value*2.54, :cm)
        end

        def to_mm
          PrintCalculator::Measurement.new(@value*25.4, :mm)
        end

        def to_ft
          PrintCalculator::Measurement.new(@value/12.0, :ft)
        end
      end

      state :cm do
        def to_in
          PrintCalculator::Measurement.new(@value*0.3937, :in)
        end

        def to_mm
          PrintCalculator::Measurement.new(@value*10, :mm)
        end

        def to_ft
          PrintCalculator::Measurement.new(@value*0.03281, :ft)
        end
      end

      state :mm do
        def to_in
          PrintCalculator::Measurement.new(@value*0.03937, :in)
        end

        def to_cm
          PrintCalculator::Measurement.new(@value*0.1, :mm)
        end

        def to_ft
          PrintCalculator::Measurement.new(@value*0.003281, :ft)
        end
      end

      state :ft do
        def to_in
          PrintCalculator::Measurement.new(@value*12, :in)
        end

        def to_cm
          PrintCalculator::Measurement.new(@value*30.48, :ft)
        end

        def to_mm
          PrintCalculator::Measurement.new(@value*304.8, :mm)
        end
      end

    end

    def initialize(value, unit)
      if unit.empty? || unit.nil?
        unit = "in"
      end
      @value = value
      @unit = unit.to_sym
      super()
    end

    def to_s
      "#{@value}#{@unit}"
    end

    # def method_missing(method, *args, &block)
    #   if [:to_ft!, :to_mm!, :to_cm!, :to_in!].include?(method)
    #     wanna_be = self.send(method.to_s[0..-2].to_sym) # strip off bang
    #     @value = wanna_be.value
    #     @unit = wanna_be.unit
    #   else
    #     super
    #   end
    # end

  end
end

But, when I create a new measurement, and try to run a conversion method, I get an error.

m = PrintCalculator::Measurement.new(8.5, :in)
=> 8.5in
m.to_cm
=> NoMethodError: super: no superclass method `to_cm' for 8.5in:PrintCalculator::Measurement

I'm sure I've just messed something up with my implementation. Any idea what I'm doing wrong here?

PS: I realize that redefining the methods over and over again isn't a very DRY approach, but I wanted to at least try and get it working before I started abstracting things.

gilbert commented Mar 31, 2012

Looking into this problem, I discovered some bugs with state_machine. But first, here's the workaround:

module PrintCalculator
  class Measurement
    # ...
    state_machine :unit, :initial => lambda {|m| m.unit } do
      # ...
    end

    # ...

  end
end

EDIT: It turns out the init_state! transition is not needed after all.

@gilbert gilbert added a commit to gilbert/state_machine that referenced this issue Mar 31, 2012

@gilbert gilbert Added tests for initializing initial state. Response to #169 612a122
Owner

obrie commented Apr 1, 2012

Hey @mindeavor -

I can't reproduce the issue you were describing in the initial report. My results:

1.9.3p125 :095 > m = PrintCalculator::Measurement.new(8.5, :in)
 => 8.5in 
1.9.3p125 :096 > m.to_cm
 => 21.59in 

I tried this on both 1.9.3 and 1.9.2 with the same results. What version of state_machine and ruby are you using? Are you testing this script within an app or in a basic irb console?

Owner

obrie commented Apr 1, 2012

It looks like you're right about the initial state, however. I'll look into getting this fixed soon.

@obrie obrie was assigned Apr 1, 2012

gilbert commented Apr 1, 2012

The initial report was written by @willcosgrove . I also didn't find an error with the original code, but I came across it when I replaced

state_machine :unit, :initial => :in do

with

state_machine :unit, :initial => lambda { :in } do

This might even be another error that isn't covered in the functional tests I wrote.

Owner

obrie commented Apr 1, 2012

Ah, sorry I misread who was reporting the bug :)

@obrie obrie closed this in d634707 Apr 23, 2012

Owner

obrie commented Apr 23, 2012

Thanks for helping out with this! Hopefully I can get a release out soon with this fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment