Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby ActiveRecord Basics AR Extensions Stateful Models

Matt Young
Matt Young
6,348 Points

State_Machine(s) Usage

It seems like there is a fork of state_machine that is now being maintained. It's state_machines (plural): https://github.com/state-machines/state_machines

However, it doesn't seem to work in quite the same way as state_machine did. At least, the code provided in the treehouse tutorial doesn't work.

Has anyone got it working?

My code is:

  state_machine :state, :initial => :submitted do
    event :approve do
      transition :submitted => :approved
    end

    event :reject do
      transition :submitted => :rejected
    end
  end

I suspect the syntax used for the first line may have changed, but I haven't been able to get it to work.

6 Answers

Jason Seifer
STAFF
Jason Seifer
Treehouse Guest Teacher

Matt Young This one took me a bit of time to figure out because there were a couple of problems here that were dependent on each other. The problem was that the Gemfile.lock had an old version of the state_machines-activerecord gem specified. In the end, you need to do the following:

bundle update state_machines-activerecord

I figured this out the following way:

  1. In the time_entries_controller, the time_entry wasn't being created but it also wasn't telling me why. I added the following code so I had an error message I could debug:
class TimeEntriesController < ApplicationController
  def create
    e = Employee.find(params[:employee_id])
    time_entry = e.time_entries.build(params.require(:time_entry).permit(:time, :account_id))
    if !time_entry.save
      logger.error time_entry.errors.full_messages
    end
    redirect_to employee_path(e)
  end
end

That told me that it wasn't being saved because I didn't have an account_id column. I created some accounts and customers but it still wasn't working.

  1. I loaded the time_entry.rb model and took a look at why validation might not be passing. Line 4 had a typo: "greter_then" rather than "greater_than".

  2. I tried again and it was silently failing. I double checked the documentation and everything appeared to be correct.

  3. I checked GitHub issues for the gem and found no closed issues that mirrored the problem.

  4. I double checked the version of the gem and it was "0.0.0" installed and "0.3.0" was the latest. I updated the gem using the bundle update command above and tried it again and it worked.

Hope that helps!

Matt Young
Matt Young
6,348 Points

Yay. Thanks Jason Seifer. That's got everything working just as it should.

Elkin Arriero
Elkin Arriero
15,160 Points

My solution:

As Jason said, I run bundle update state_machines-activerecord, but it did not work, so what I did was:

  1. In Gemfile I deleted: gem 'state_machines'.
  2. Added this gem: gem 'state_machines-activerecord'
  3. And then execute: $ bundle
  4. Run the server again.

And it worked like a charm...

Hope it helps :)

Elkin Arriero
Elkin Arriero
15,160 Points

Oppsss... My bad, in Gemfile you should keep: gem 'state_machines', and just follow from step no. 2.

I replaced gem 'state_machine' from the video with gem 'state_machines-activerecord'.

bundle install showed three new gems loaded:

Installing state_machines 0.4.0
Installing state_machines-activemodel 0.3.0
Installing state_machines-activerecord 0.3.0

Restart your rails server, of course.

Everything worked after that.

Jason Seifer
STAFF
Jason Seifer
Treehouse Guest Teacher

Hey Matt Young! Any chance you can paste the rest of your code as well as the error you're getting?

Matt Young
Matt Young
6,348 Points

Here's what I've done so far for this lesson.

  1. Add: gem 'state_machines' to Gemfile, and then run bundle.
  2. Add new column (state) in account_entries table.
  3. Modify account_entry.rb as follows:
class AccountEntry < ActiveRecord::Base
  belongs_to :account

  validates :account_id, presence: true
  validates_associated :account

  after_save :update_account_balance!

   state_machine :state, :initial => :submitted do
    event :approve do
      transition :submitted => :approved
    end

    event :reject do
      transition :submitted => :rejected
    end
  end

  def update_account_balance!
    account.update_balance!
  end

end

Following along with the lesson, I then fire up the server, visit http://localhost:3000/employees/2 and create a new time entry. A time entry is added, but it doesn't enter anything in the account_entries table for 'state'.

If I remove the state_machine code from account_entry.rb then the entries in the 'state' column will display. Otherwise they do not.

Is that all the code you require?

Jason Seifer
STAFF
Jason Seifer
Treehouse Guest Teacher

Matt Young Try adding gem 'state_machines-activerecord' to your Gemfile and give it another shot. That contains the code to make it work with ActiveRecord models. It should work after that. If not, please reply to this thread and tag me again! Thanks!

Matt Young
Matt Young
6,348 Points

Hi Jason Seifer,

First up, how do I 'tag' you?

I'm now using state_machines-activerecord in my Gemfile, instead of 'state_machines', which I removed.

And now, when I access http://localhost:3000/employees/2 I get:

NoMethodError in Employees#show

Showing C:/Users/Matt/Websites/RubyOnRails/TestApp/biller2/app/views/employees/show.html.erb where line #4 raised:

undefined method `state_machine' for #<Class:0x876fd68>

Rails.root: C:/Users/Matt/Websites/RubyOnRails/TestApp/biller2 Application Trace | Framework Trace | Full Trace

app/models/account_entry.rb:9:in <class:AccountEntry>' app/models/account_entry.rb:1:in<top (required)>' app/models/time_entry.rb:1:in <top (required)>' app/views/employees/show.html.erb:4:inapp_views_employees_show_html_erb__507868244_71086140'

Here's my code for the relevant files:

account_entry.rb

class AccountEntry < ActiveRecord::Base
  belongs_to :account

  validates :account_id, presence: true
  validates_associated :account

  after_save :update_account_balance!

   state_machine :state, :initial => :submitted do
    event :approve do
      transition :submitted => :approved
    end

    event :reject do
      transition :submitted => :rejected
    end
  end

  def update_account_balance!
    account.update_balance!
  end

end

time_entry.rb

class TimeEntry < AccountEntry
  belongs_to :employee

  validates :time, numericality: {greter_than: 0, less_than: 24}

  before_create :calculate_amount!

  def self.per_hour
    100
  end

  def calculate_amount!
    self.amount = TimeEntry.per_hour * self.time
  end

end

show.html.erb

<h1><%= @employee.name %></h1>

<h3>Submit time form</h3>
<%= form_for [@employee, TimeEntry.new] do |f| %>
  <%= f.label :customer %>
  <%= f.collection_select(:account_id, Customer.premier.all, :id, :name) %>
  <%= f.label :time %>
  <%= f.text_field :time, :value => 1.0 %>
  <%= f.submit %>
<% end %>

<h3>Time Entries</h3>

<% @employee.time_entries.each do |entry| %>
  <div class="entry">
    <strong><%= entry.state %></strong>
    <%= entry.time %>
    <%= entry.account.name %>
    <%= entry.created_at %>
  </div>
<% end %>
Jason Seifer
STAFF
Jason Seifer
Treehouse Guest Teacher

Hi Matt Young I'm sorry for all the trouble here. At this point it looks like the state machine gem isn't even being recognized. Is there any way you could zip up your whole project and paste a link to that here so I can download it and take a look? There are a lot of parts here that I need to look at and it may be quicker than going back and forth on this thread.

David Chapman
David Chapman
22,023 Points

Elkin's steps solved the issue for me.

I did not need to add gem 'state_machine' to my gem file, only gem 'state_machines-activerecord'.

Thanks!