Just Do It: Learn Sinatra, Part Two

Tweet

In part one of this tutorial, we set Sinatra up and displayed some pages. Now the fun really starts – we will be using a database to store our tasks in.

In this tutorial we will be using SQLite for the local database. As its name suggests, this is a lightweight file-based database that doesn’t require any configuration. If you haven’t already got this installed, then see this page for some simple instructions.

In order to interact with the database, I will be using DataMapper. This is a ORM that works in a similar way to Active Record, but has a slightly different methodology and syntax.

In order to use DataMapper with SQLite, the following gems will need to be installed:

$> gem install data_mapper dm-sqlite-adapter sqlite3

Next, we need to make sure that the DataMapper gem is required at the top of the main.rb file:

require 'sinatra'
require 'data_mapper'

Now we have to set up the database connection which is just one line of code to tell DataMapper that we will use a SQLite database called ‘development.db’ and it will be saved in the same folder as the app:

DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/development.db")

The Task Model

In order to save tasks to the database we will need to create a task class. The following code goes beneath the database connection in main.rb:

class Task
  include DataMapper::Resource
  property :id,           Serial
  property :name,         String, :required => true
  property :completed_at, DateTime
end
DataMapper.finalize

The Task class is linked to DataMapper by the line include DataMapper::Resource which includes the DataMapper Resource module as a mixin – this is how you make any Ruby Class a DataMapper resouces. After this line, comes properties of the Task class. The :id property is of type Serial which gives each task an auto-incrementing identifier. After that, we only really need two more properties for our tasks – the name of the task (notice that I’ve made this a required property) and the completed date. The `DataMapper.finalize` method is used to check the integrity of your models. It should be called after ALL your models have been created and before your app starts interacting with them. We only have one model at the moment so it can just go at the end of the class definition.

Adding Tasks

To get us started, we are going to use IRB to create a few tasks. Open up a command line prompt and type the following to access the app:

$> irb
ruby-1.9.2-p180 :002 > require './main'
DataObjects::URI.new with arguments is deprecated, use a Hash of URI components (/home/daz/.rvm/gems/ruby-1.9.2-p180/gems/dm-do-adapter-1.1.0/lib/dm-do-adapter/adapter.rb:231:in `new')
=> true

You can ignore the warning that appears when you require the file. This is due to a bug in DataMapper that is due to be fixed in the next release. Don’t worry, it doesn’t affect the app in any way.

Before we can add any tasks, we need to set up the Tasks table by migrating the model. This is done using the following command:

ruby-1.9.2-p180 :003 > Task.auto_migrate!
=> true

Now we are ready to add some tasks. This can be done in irb using the create method. Here are a few examples of adding a few tasks:

ruby-1.9.2-p180 :004 > Task.create(name: "get the milk")
=> #
ruby-1.9.2-p180 :005 > Task.create(name: "order books") => #
ruby-1.9.2-p180 :006 > Task.create(name: "pick up dry cleaning")
=> #
ruby-1.9.2-p180 :007 > Task.create(name: "phone plumber")
=> #

Those tasks have now been saved to the database. If we want to see them, we just need to make a couple of small changes to the handler in main.rb that deals with the root url:

get '/' do
  @tasks = Task.all
  slim :index
end

Task.all gets all the tasks from the database and stores them as an array in the instance variable @tasks. Recall that instance variables are available to all the views, so I use this in the index.slim view to iterate through each task and display them as a list:

form action="/" method="POST"
  input type="text" name="task[name]"
  input.button type="submit" value="New Task >>"

h2 My Tasks

ul.tasks
  - @tasks.each do |task|
    li.task= task.name

Start up the server by typing $> ruby main.rb at a command prompt, reload the page and you should see a nice list of tasks. This is all well and good, but we want to be able to add the tasks from the web page. This isn’t a problem – in fact, we already have a form to do it, we just need to plug it in to DataMapper so that the tasks are saved to the database.

In part one of the tutorial, we had the following handler that was used to process the POST request when the form was submitted:

post '/' do
  @task =  params[:task]
  slim :task
end

This just needs changing slightly to the following:

post '/' do
  Task.create  params[:task]
  redirect to('/')
end

This creates a new task using the information stored in the params hash that was submitted in the form (in this case it is just the name of the task). It then redirects back to the home page (using a GET request this time), which should show the list of tasks including the new task. Give it a try in your browser.

Deleting Tasks

So far, so good, we can add tasks to our list, but how about deleting them? To do this we need to add a delete button to each task that will send a DELETE request to the server. While I’m at it, I’m going to separate the view code for tasks into its own separate file. Put the following code into a file saved as ‘task.slim’ in the views folder:

li.task id=task.id
  = task.name
  form.delete action="/task/#{task.id}" method="POST"
    input type="hidden" name="_method" value="DELETE"
    input type="submit" value="×"  title="Delete Task"

Now change index.slim to the following:

form action="/" method="POST"
  input type="text" name="task[name]"
  input.button type="submit" value="New Task >>"

h2 My Tasks

ul.tasks
  - @tasks.each do |task|
    == slim :task, locals: { task: task }

There are a few things to notice here. Firstly, in the task view, we need to create a form for the delete button. This is so that we can include a hidden field with the attributes name="_method" value="DELETE". This is because most browsers don’t understand the HTTP verbs PUT and DELETE, only GET and POST. To get round this, Sinatra uses hidden form fields to ‘fake out’ the browser and process the request correctly. This means that when the delete form is submitted, it will be processed as a DELETE request.

In the index view, notice how I referenced the task view using the following line:

    == slim :task, locals: { task: task }

This is how Sinatra handles partials – you just call a template inline. In this case I need to tell it that the reference I make to task in the task.slim file is the same as the task that is being passed to the block.

All that is left to do is to write a handler for deleting tasks. You’ll notice that the url is given in the action attribute of the form as /task/#{task.id}. The corresponding handler looks like this:

delete '/task/:id' do
  Task.get(params[:id]).destroy
  redirect to('/')
end

This code simply finds the task that was clicked on by using the id in the url and then uses the destroy method to delete it from the database. It then redirects to the root page where the remaining tasks will be listed.

Completing Tasks

To finish off, we just need to be able to complete the tasks. Remember when we created the Task class, we specified a completed_at property which tells us the date that the task was completed on. If the property is set to nil, then we can assume that the task has not been completed. Open up task.slim and change it to the following:

li.task
  = task.name
  form.update action="/task/#{task.id}" method="POST"
    input type="hidden" name="_method" value="PUT"
    -if task.completed_at.nil?
      input type="submit" value="  " title="Complete Task"
    -else
      input type="submit" value="✓" title="Uncomplete Task"
  form.delete action="/task/#{task.id}" method="POST"
    input type="hidden" name="_method" value="DELETE"
    input type="submit" value="×" title="Delete Task"

We’ve now added a line to say when the task was completed, if it has been, and another form that contains a hidden form field specifying that this is a PUT request. This is because we are modifying the task’s properties (actually, a PATCH request should be used, but this isn’t supported until Sinatra 1.3). We also display a different button depending on whether the task has been completed or not. If a task has been completed then it shows a check mark, if it hasn’t been completed then it looks like an empty check box, so there is some visual feedback to show a task’s status.

Notice that the url given in the action attribute is exactly the same – this is because Sinatra will react differently to whichever HTTP verb is used. We now need to write a handler to deal with this request, so add this to the bottom of main.rb:

put '/task/:id' do
  task = Task.get params[:id]
  task.completed_at = task.completed_at.nil? ? Time.now : nil
  task.save
  redirect to('/')
end

This finds the task based on the id in the url then sets the completed_at property to the current time if it hasn’t been set already, or reverts it back to nil if it has been set. In other words, the button will act as switch to toggle between the task being done and not done.

Restart the server and have a go at adding, deleting and completing tasks. We now have a fully functioning to do list – all in under 40 lines of code! It’s a bit rough around the edges and doesn’t look the best, but we will sort that out as well as adding multiple lists in part 3!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.larrylv.com Larry Lv

    Nice article!
    I have been learning & using Sinatra for a few weeks.
    It’s really cool!

    • http://ididitmyway.heroku.com/ Darren Jones

      Thanks for the kind feedback Larry.

      I’m glad that you’re enjoying using Sinatra – it rocks!

      DAZ

  • http://arunanskanthan.com Arunan

    Yay! Thanks for the new one… :)

    • http://ididitmyway.heroku.com/ Darren Jones

      You’re welcome buddy!

      DAZ

  • http://opalab.com Oto Brglez

    Hi DAZ! Nice two articles about Sinatra. Love ti!

    Perhaps for the 4th part you could mix RSpec with Sinatra…

    – Oto

    • http://ididitmyway.heroku.com/ Darren Jones

      Hey Oto,

      Thanks for the feedback. The 4th part is about deploying the app to Heroku. Not tests in this series of tutorials unfortunately. In fact I need to improve my use of Rspec, so maybe that would make for a good follow up later.

      cheers,

      DAZ

  • Andrea

    Great article DAZ! I’m loving what I’m seeing about Sinatra!

    • http://ididitmyway.heroku.com/ Darren Jones

      Thanks Andrea! I’m really glad you’re enjoying it and hope you like the next two parts just as much.

      DAZ

  • http://agilesense.com rafidude

    Great article! I prefer working with Sinatra because it is small, beautiful and mostly avoids magic (compared to Rails).

    While working through the example, I found a couple of minor mistakes.
    1) Instead of require ‘datamapper’ it should be require ‘data_mapper’
    2) In the Post ‘/’ method, the task creation code should be Task.create name:task

    Most Sinatra tutorials are way too basic. Your series on Sinatra is perfect for intermediate Ruby developers and provides enough material to start building non-trivial data driven web apps. Thank you. I am looking forward to other parts in this series.

    -rafidude

    • http://ididitmyway.heroku.com/ Darren Jones

      Hi Rafidude,

      Thanks for the feedback. I find that Sinatra has just enough magic to let you interact with the server, but not so much that you don’t know what’s going on in your own app, the perfect balance basically.

      With regard to your comments:
      1. I think you can use either of the DataMapper gems, require ‘datamapper’ definitely works, the gem is here: http://rubygems.org/gems/datamapper

      2. Whoops, that was a careless omission, but I was actually going for a slightly different way of creating the task using a nested hash of task properties. The code in the form was wrong, it should have been:

      input type=”text” name=”task[name]”

      I’ve updated the article to this now, thanks for spotting it. Your way also works, but I like to use a task hash of properties in case there are more in the future, that way I can always just use Task.create params[:task] regardless of whether I have added any new properties in the form.

      I’m glad you are liking the level of the tutorials and I hope you enjoy the rest of the series!

      cheers,

      DAZ

      • David

        If you say to install data_mapper, don’t try to require datamapper. I think you must be more consistent, you are confusing people at this point.

  • Jack

    Hi Daz

    Thanks for the tutorials – i’m new to Ruby and Sinatra so this is just what I needed to help me with my study.

    I followed this tutorial but keep getting an uninitialized constant error when starting the database – can you see what the problem is from my code below?

    Many thanks in advance

    Error output:
    main.rb:10:in `': uninitialized constant Task::Resource (NameError)
    from main.rb:9:in `’

    – – – – – – – – – – –

    require ‘sinatra’
    require ‘slim’
    require ‘data_mapper’

    DataMapper.setup(:default, ENV['DATABASE_URL'] || “sqlite3://#{Dir.pwd}/development.db”)

    class Task
    include DataMapper:Resource
    property :id, Serial
    property :name, String, :required => true
    property :completed_at, DateTime
    end

    get ‘/’ do
    @tasks = Task.all
    slim :index
    end

    get ‘/:task’ do
    @task = params[:task].split(‘-‘).join(‘ ‘).capitalize
    slim :task
    end

    post ‘/’ do
    Task.create params[:task]
    redirect ‘/’
    end

    • Jack

      Ah – I was missing the double colon in DataMapper::Resource ….

      • http://ididitmyway.heroku.com/ Darren Jones

        Glad you sorted it out Jack! Thanks for the kind words about the article – I hope you enjoy learning Ruby and Sinatra!

        DAZ

  • http://hunterhusar.net Hunter

    Hey great tutorial thank you so much. I am so noob but I just noticed a minor thing, I think, after you say:

    Now change index.slim to the following:

    form action=”/” method=”POST”
    input type=”text” name=”task”
    input.button type=”submit” value=”New Task >>”
    h2 My Tasks
    ul.tasks
    – @tasks.each do |task|
    == slim :task, locals: { task: task }

    …it’s supposed to be

    task[name] and not just task

    right?

    • http://ididitmyway.heroku.com/ Darren Jones

      Hi Hunter,

      Well spotted, thanks for that – I’ve updated the post.

      cheers,

      DAZ

  • http://johnturknett.com John

    I did not know you could use irb to load your Sinatra environment. That will help out a lot. Thanks for the info.

    • http://ididitmyway.heroku.com/ Darren Jones

      Hi John,

      I use irb loads to interact quickly with the db – it’s invaluable. Glad you found it useful.

      cheers,

      DAZ

  • Sean

    Would there be a way to do this with a mysql database instead of SQLite? I ask because I already have Mysql up and running.

    • http://ididitmyway.heroku.com/ Darren Jones

      Definitely Sean! You just need to install the mysql adapter gem:

      gem install dm-mysql-adapter

      Then all you need to do is change the database connection string to:

      DataMapper.setup(:default, ENV['DATABASE_URL'] || ‘mysql://localhost/the_database_name’)

      More details can be found on the DataMapper website:
      http://datamapper.org/getting-started

      Having said that, you might want to give sqlite a try for development as it’s very easy to use, has a very small footprint and doesn’t require a server to be running.

      Hope that helps,

      DAZ

  • Chris

    Hi,
    Thanks for the fantastic tutorial.

    I’ve got this all working but I notice that the web-server is logging an error when fetching the index page:

    NameError – undefined local variable or method `task’ for #:

    This seems to be related to using slim to render the partial:

    ul.tasks
    – @tasks.each do |task|
    == slim :task, locals: { task: task }

    It all works OK, each task in the to-do list appears and can be deleted. The partial code can certainly access the task object but I don’t know why the undefined local variable error is appearing in the log?!

    Any ideas?

    Kind Regards,
    Chris

  • Keith

    found that I had to
    Task.create(name: params[:task])

    instead of
    Task.create params[:task]

    in post method

    • http://ididitmyway.heroku.com/ Darren Jones

      Hi Keith,

      Is your reply to Chris above or are they not related at all?

      DAZ

  • Chris

    Hi,
    I don’t think they are related :(

    I’m seeing the error in the log when I look at the index page (GET ‘/’ etc’). Here is more of the trace:

    NameError – undefined local variable or method `task’ for #:
    /home/chris/Study/JustDoIt/views/task.slim:1:in `evaluate_source’
    /home/chris/.rvm/gems/ruby-1.9.2-p290@JustDoIt/gems/tilt-1.3.3/lib/tilt/template.rb:209:in `instance_eval’

    It’s strange because the tasks are actually listed (using the partial) and I can delete them etc so the partial code (task.slim) is working.

    King Regards/Chris

    • http://ididitmyway.heroku.com/ Darren Jones

      Chris, try changing the partial to:

      ul.tasks
      – @tasks.each do |task|
      == slim :task, :locals => { :task => task }

      I think that using the old hash-rocket syntax might make it work.

      DAZ

    • baiki

      OK, guess I found Chris’ and my error in the log file. If you start with lesson one, moving on to lesson two and forgetting to remove this part from main.rb:

      # get ‘/:task’ do
      # @task = params[:task].split(‘-‘).join(‘ ‘).capitalize
      # slim :task
      # end

      then you will see this error in terminal output:

      NameError – undefined local variable or method `task’ for #:

      Roger roger. Moving on to “Part 3″

      Cheers,
      Baiki

  • John

    I am stuck. I am not sure what to post to help out.

    I could pastebin it for you.

    I am using JRuby not ‘C’ ruby. But I have never had an issue before.

    John

    • http://ididitmyway.heroku.com/ Darren Jones

      Hi John,

      Sorry to hear that you’re stuck. If you send a pastebin and the specific error message then I’ll do my best to help you out.

      DAZ

  • John

    Well I found out what the problem was. I dont quite understand it. But the fix is this:

    Where you use .get method I changed it to .find and it all works great.

    I am using Sinatra on JRuby with Active Record. Here is the code changes:

    require ‘rubygems’
    require ‘sinatra’
    require ‘active_record’
    require ‘haml’

    ## Setup
    configure do
    ActiveRecord::Base.establish_connection({
    :adapter => “jdbcsqlite3″,
    :dbfile => “tasks.sqlite”
    })
    if File.exists?(‘tasks.sqlite’)
    ## Dont remake database file
    else
    ActiveRecord::Schema.define do
    create_table “tasks”, :force => true do |t|
    t.column “name”, :varchar
    t.column “completed_at”, :timestamp
    end
    end
    end
    end

    ## Models
    class Task nil)
    end

    def link
    “#{self.name}”
    end
    end

    Didnt paste it all, just the DB parts I changed. Changing get to find doesnt need pasting. :)

    Let me know what you think.

    BTW, I use AR since I cannot seem to get dm to work on JRuby. But AR works great. )

    Thanks,
    John

  • http://www.abujamaps.com chimere

    Hi Darren,
    I also had the same problem Chris was experiencing till i used the old hash rockets method, i was using default ruby 1.8.7 on my mac OSX 10.7 then i switched to 1.9.7 using rvm and still experienced the same issues till you pointed out the hash rockets approach, you might want to put a caveat in the tutorial.

    Thank you

  • http://www.abujamaps.com chimere

    Hey, can’t seem to add tasks i can delete and complete tasks but adding tasks gives me “undefined method `include?’ for nil:NilClass” my task variable {“name”=>”boo”}, the traceback highlights the post method “Task.create params[:task]“.

    #! /usr/bin/env ruby

    require ‘rubygems’
    require ‘bundler/setup’
    require ‘sinatra’
    require ‘sqlite3′
    require ‘data_mapper’
    require ‘dm-core’
    require ‘dm-sqlite-adapter’
    require ‘slim’

    DataMapper.setup(:default, ENV['DATABASE_URL'] || “sqlite3://#{Dir.pwd}/just_do_it.db”)

    class Task
    include DataMapper::Resource

    property :id, Serial
    property :name, String, :required => true
    property :completed_at, DateTime
    end

    get ‘/’ do
    @tasks = Task.all
    slim :index
    end

    get ‘/:task’ do
    @task = params[:task].split(‘-‘).join(‘ ‘).capitalize
    slim :task
    end

    post ‘/’ do
    Task.create params[:task]
    redirect ‘/’
    end

    delete ‘/task/:id’ do
    Task.get(params[:id]).destroy
    redirect ‘/’
    end

    put ‘/task/:id’ do
    task = Task.get params[:id]
    task.completed_at = task.completed_at.nil? ? Time.now : nil
    task.save
    redirect ‘/’
    end

    • davidbe

      I have the same problem, even in irb. Migrating helps – but also destroys the present data.

      $ irb
      > require ‘./main.rb’
      > Task.create(name: “phone plumber”)

      gives this error:

      irb(main):002:0> Task.create(name: “phone plumber”)
      NoMethodError: undefined method `include?’ for nil:NilClass
      from /usr/lib/ruby/gems/1.9.1/gems/dm-core-1.2.0/lib/dm-core/resource.rb:332:in `block in attributes=’
      from /usr/lib/ruby/gems/1.9.1/gems/dm-core-1.2.0/lib/dm-core/resource.rb:329:in `each’
      from /usr/lib/ruby/gems/1.9.1/gems/dm-core-1.2.0/lib/dm-core/resource.rb:329:in `attributes=’
      from /usr/lib/ruby/gems/1.9.1/gems/dm-core-1.2.0/lib/dm-core/resource.rb:748:in `initialize’
      from /usr/lib/ruby/gems/1.9.1/gems/dm-validations-1.2.0/lib/dm-validations.rb:129:in `new’
      from /usr/lib/ruby/gems/1.9.1/gems/dm-validations-1.2.0/lib/dm-validations.rb:129:in `create’
      from (irb):2
      from /usr/bin/irb:12:in `’

      but then

      > Task.auto_migrate!
      > Task.create(name: “phone plumber”)

      gives

      => #

      and works… but the previous existing data is wiped. I’ve no clue how to solve this…

      Any help?
      Kind regards,
      david

      • davidbe

        UPDATE: solved it myself!

        You need to add this line to main.rb (after class Task … end):

        Task.auto_upgrade!

        Kind regards,
        david

        • http://www.clintlaskowski.com Clint Laskowski

          Thanks, davidbe. Your solution — to add ‘Task.auto_upgrade!’ as a line after the class definition — worked for me, too :-) I was starting to pull my hair out on that one.

          • indigo

            Hello,

            I was going through this tutorial today, and I believe that DataMapper’s documentation states that you should call DataMapper.finalize after declaring your models. This should be the correct way to do it, and then you won’t need to use auto_upgrade.

            As well, it would be helpful if the tutorial could be updated with the correct name of the data_mapper gem in the require statement (require ‘data_mapper’ instead of require ‘datamapper’).

            Otherwise, thank you for the great tutorial!

          • http://ididitmyway.heroku.com/ Darren Jones

            Hi Indigo,

            Thanks for the comment.

            I’ve updated the tutorial now to use DataMapper.finalize (I don’t think it was required when I first wrote the tutorial, but it is now).

            I don’t think this will mean `auto_upgrade!` isn’t needed though. `auto_upgrade!` applies migrations but keeps the data in the database intact. `auto_migrate!` wipes all the data in the database while it does the migration.

            I think you can use either datamapper or data_mapper. To be honest, this pulls in all the official plugins, so I usually just require dm-core and only the plugins that I need.

            cheers,

            DAZ

  • Seamus

    I often got an annoying random bug when viewing the app after starting shotgun.

    The HTML would load but not be visible, unless I went into Chrome DOM Inspector and clicked on an element in there.

    The fix is to add a blank sreen.css stylesheet in the public directory.

    Great and useful tutorial BTW. Not too oversimplified or too deep and off topic. Also the Sinatra tutorial on About.com is very good too.

    • http://daz4126.com/ Darren Jones

      I’ve never seen this bug Seamus, but thanks for posting the fix for it.

      Thanks for the feedback too – glad you found the tutorial useful.

      DAZ

  • aaron

    Now it’s my turn, I have no idea what I’ve done wrong, when adding tasks I get the following: https://gist.github.com/2271487 – I’ll keep googling but I daresay I may struggle to find an answer.

    • aaron

      nevermind, i had to change it to data_mapper when requiring

  • Tatiana

    I routinely find that this logic can screw up and give the following error:

    “Unexpected error while processing request: negative argument”

    Problem is it’s not clear what causes this. For example, I had everything working up until the completing task part. Then I added that and I started getting the above error.

    So I took that part out (I had it versioned with Git) and then went back to just up to deleting. The error kept happening. So then I though the database had gotten corrupted. So I deleted that and then things started working again.

    Except that when posting a task, I would get three errors in a row:

    !! Unexpected error while processing request: negative argument
    !! Unexpected error while processing request: negative argument
    !! Unexpected error while processing request: negative argument

    I don’t see this with any other examples of Sinatra that I work with, so I’m not sure what the problem is. It seems to be whenever the “partial” logic is included that the problems at least start to happen.

  • baiki

    @DAZ: Wonderful tutorial. Love it! However I got stuck too. Did not yet find solution for Chris previous error. Got same error. Assuming we use too different version of gems. I’ll keep digging…

    Cheers,
    Baiki

    https://gist.github.com/2587941
    https://gist.github.com/2587949
    https://gist.github.com/2587953
    https://gist.github.com/2587957

  • http://cloudafrica.net Len

    I am also getting the “negative argument” error when replacing erb with slim files – any pointers welcome.

  • http://daz4126.com/ Darren Jones

    Hi guys,

    Sorry that you’re getting this error. When specifically is it happening? Can you get it to happen on the live site (http://justdoit.heroku.com/)? It might be that a gem has been updated and is breaking the code (the gems on the live site will be the versions from when the article was written). If you can give me a few more details so that I can reproduce the error then I’ll have a go at fixing it.

    cheers,

    DAZ

  • Ken H

    You may have to try require ‘data_mapper’ for this to work.

  • Louise

    Great tutorial, however there is a small typo: when you require ‘data_mapper’ there needs to be an underscore between data and mapper.

  • ragu

    Minor correction: The require should say

    require ‘data_mapper’

    instead of

    require ‘datamapper’

  • http://daz4126.com/ Darren Jones

    Thanks for the comments about ‘datamapper’ guys. At the time of writing I think there was a ‘datamapper’ gem, but I’ve changed it to ‘data_mapper’ now so it works.

    cheers,

    DAZ

  • Lee

    I got a similar error to some other people on the Task.create method. It was complaining that it couldn’t find an “each” method. Thinking through what I was guessing it was doing, I simply changed the input to “create” to be a hash, ie Task.create name: params[:task] and this fixed my problem and then everything worked. Not sure if this is a new version of data_mapper which is causing this or what. Doing Task.auto_upgrade! did not seem to help me but I am fine with what I found to work.

    Thanks for the walkthrough

  • C. H.

    Where’s part 3? Link is missing…