Search code examples
rubysinatrasequel

Getting Sequel associations through Sinatra


I'm trying to return json-formatted data from my Sinatra REST API. I currently have a bunch of associations set up, but I'm having trouble getting the views I want from my API despite getting them easily in Ruby.

For example, from my tables:

DB.create_table?(:calendars) do
  primary_key :id

end

DB.create_table?(:schedules) do
  primary_key :id

  foreign_key :resource_id,  :resources
  foreign_key :task_id, :tasks
  foreign_key :calendar_id,  :calendars
end

In Ruby, I'm able to run a block like this and display all the info I need through my associations:

Calendar.each do |c|
  c.schedules.each do |s| 
    puts "RESOURCE ##{s.resource_id}"
    s.tasks.each do |t|
      p t
    end
    puts
  end
end

the c.schedules call works because my calendar model contains a one_to_many :schedules association.

Now, I'm wondering how this translates to my Sinatra API. In my simple GET route, I've tried many variations trying to get the schedules associated with a calendar, and convert it to JSON:

get '/calendars' do
    c = DB[:calendar].first
    c.schedules.to_json
    content_type :json
end

... but I'll end up with an error like undefined method 'schedules' for {:id=>1}:Hash

So it looks like it's returning a hash here, but I've tried a bunch of stuff and haven't figured out how I'm supposed to work with my associations in Sinatra. How can I do this?

Thanks!


Solution

  • The reason your first block works but the second doesn't is because in the first case, you're using a Sequel model instance of class Calendar, whereas in the second case you're using a Sequel dataset.

    When you iterate over Calendar.each do |c|, the c variable gets populated with an instance of a Calendar class Sequel model object. This object has relationship methods defined (one_to_many) and you're able to query schedules and run other model methods on it.

    However, c = DB[:calendar].first gets you a Sequel dataset. This object is different than a model instance, it returns a standard Ruby hash (or an array of hashes).

    You can change your 2nd block to use a model instead and it will get the result you want:

    get '/calendars' do
        c = Calendar.first # <=== CHANGE FROM DATASET TO MODEL
        c.schedules.to_json
        content_type :json
    end