Nested resources

I have many groups and each group has one timetable and I wish to retrieve the timetable for a particular group and have a method called get_timetable_for_group, then where should this method go to? Inside the groupscontroller or the timetablescontroller as get_timetable_for_group is a complex method.

Currently I use this in my routes.rb

  resources :groups do
    resource :timetable
    collection do
      get 'get_group_code'
      get 'get_group_name'
      get 'find_by_name'
    end
  end

so should I add get 'find_timetable" also inside collection? And have a method inside groupscontroller as


def find_timetable
#complex logic
end

or inside the timetablescontroller as



def find_timetable_for_group
#complex logic
end

Hello. I have a few questions:

  • How does your Group model know about it’s Timetable? Is this a Rails has_one relationship?
  • On which page/action do you need to display the timetable for the group?

If I understand what you are doing, then I think having a TimetablesController#show action which retrieves the one timetable you want to display on the page makes sense.

If the logic for retrieving a Group’s timetable is complex, I’d put that method on Group itself, and have the controller get the right timetable by asking the group instance for it.

Yes there many groups and each group has only one timetable.
One the users page there are many groups. He can click on any group which then retrieves the timetable of that group and displays the timetable on that page itself using ajax.

Also each timetable itself has many timetable_entries. And each timetable_entry has reference to a particular subject,teacher created on the group which makes the logic complex because I have to send back a json of the timetable

I think it would make sense for the click that generates the request asking for the timetable to be to a URL like:

/groups/123/timetable.json - which would route to a TimetablesController#show action, and return the JSON.

Within that action, something like…

class TimetablesController
  def show
    @timetable = group.timetable
    # return json...
  end

  private
  def group
    Group.find(params[:group_id])
  end
end

And where does the return json logic go to? because timetable itself has many other classes.

Each timetable basically has many teachers, many subjects, many rooms allocated. Each timetable has many timetable_entries. Each timetable_entry has only one teacher,one subject, one room. So for creating the json I have to first gather all the timetable_entries for the timetable based on the id of the timetable, get to the timetable_entry, find the teacher, subject room for that entry. Do this for each timetable_entry, gather them up and send back a json.

Does it make sense to move the has_many teachers to the group model.

Maybe a Timetable#to_json method on the Timetable model which knows which JSON it needs to assemble to correctly represent the timetable data?

Thanks @mjankowski. I definitely like your approach. I will try it tonight.

but currently I am getting issue in the bullet gem
N+1 Query detected
TimetableEntry => [:class_timing]
Add to your finder: :include => [:class_timing].
I do timetable.timetable_entries. So should this become timetable.timetable_entries.include(:class_timing)

Did you try that?

Yeah. I have redefined the get_timetable_for_group method with the show method. And now access this method using groups/:id/timetable.json call. Also for creating a new timetable I do a post to /groups/:id/timetable.

But for each group created I am generating a unique group code based on earlier created groups which is now a 6 digit word which I am generating when the group gets created. And I am using this group_code instead of the group_id for each behaviour. Or you can say my group_code is used as a primary key here though I havent defined it as a primary key on the database.

So the issue is when I am inside my def show method I have to use
group_code = params[:group_id] and then do
group = Group.find_by_group_code(group_code).

How can I change so that a symbol group_code is displayed inside my params instead of group_id

Also below is my code for the def show method and I could not understand how to use to_json for this.
def show
p params
group = params[“group_id”]
p group
group = Group.find_by_group_code(group)
time = group.timetable
weekdays =
batches =
field_entries =
field_entry = {}
field_entry[“name”] = “”
field_entry[“values”] =
if time
fields = time.fields
f =
entries_array =

fields.each do |field|

  f.push(Class.new(ActiveRecord::Base) do
    self.table_name = field.name
    #belongs_to :timetable_entry
  end)

end

  entries = time.timetable_entries.includes(:class_timing).includes(:weekday).includes(:small_group)
  entries.sort{ |a, b|

    a.class_timing <=> b.class_timing



  }





  check = []
  i = 0
  p "LLLLLLEEEEEE"
  p entries.length

  while i < entries.length
    e = []
    #fir st sort the elements baed on the classtimings only. and then group basd on them and then do the below sorting
    #each element has not been met with the condition so found = false. and when the condition meets we have found a match and we add the element t an array that met the condn.
    # but we dont add the parent element
    found = false
    entry = entries[i]


    a = entry


    j = i

    while j < entries.length

      b = entries[j]




      if a.weekday == b.weekday and a.class_timing == b.class_timing && a.small_group != b.small_group
        found = true
        entry_hash = {}
        entry_hash["weekday"] = b.weekday.name
        weekdays <<  b.weekday.name
        f.each do |field|
          d = field.where("P_Id" => b.id)
          p "DDDDDDDD"

          entry_hash[field.table_name] = d.first.name if d.length > 0
          field_entry = {}
          field_entry["name"] = field.table_name
          field_entry["values"] = field.uniq.pluck(:name)
          field_entries << field_entry
        end
        entry_hash["batch"] = b.small_group.name
        batches << b.small_group.name
        entry_hash["to_hours"] = b.class_timing.to_hours
        entry_hash["to_minutes"] = b.class_timing.to_minutes
        entry_hash["from_minutes"] = b.class_timing.from_minutes
        entry_hash["from_hours"] = b.class_timing.from_hours
        e << entry_hash

        check << entry_hash
      end
      j = j + 1
    end
    #agar ek baar bhi true hua toh element ko array me daaldo. aur agar found nahi mila toh usey seedhe parent array me daalo
    if found
      entry_hash = {}
      entry_hash["weekday"] = a.weekday.name
      weekdays <<  a.weekday.name
      f.each do |field|
        d = field.where("P_Id" => a.id)



        entry_hash[field.table_name] = d.first.name if d.length > 0
        field_entry = {}
        field_entry["name"] = field.table_name
        field_entry["values"] = field.uniq.pluck(:name)
        field_entries << field_entry

      end
      entry_hash["batch"] = a.small_group.name
      batches << a.small_group.name
      entry_hash["to_hours"] = a.class_timing.to_hours
      entry_hash["to_minutes"] = a.class_timing.to_minutes
      entry_hash["from_minutes"] = a.class_timing.from_minutes
      entry_hash["from_hours"] = a.class_timing.from_hours
      e << entry_hash

      check << entry_hash
      #p e
      entries_array << e
      #p entries_array
    else

      entry_hash = {}
      entry_hash["weekday"] = a.weekday.name
      weekdays <<  a.weekday.name
      f.each do |field|
        d = field.where("P_Id" => a.id)


        entry_hash[field.table_name] = d.first.name if d.length > 0
        field_entry = {}
        field_entry["name"] = field.table_name
        field_entry["values"] = field.uniq.pluck(:name)
        field_entries << field_entry

      end
      entry_hash["batch"] = a.small_group.name
      batches << a.small_group.name
      entry_hash["to_hours"] = a.class_timing.to_hours
      entry_hash["to_minutes"] = a.class_timing.to_minutes
      entry_hash["from_minutes"] = a.class_timing.from_minutes
      entry_hash["from_hours"] = a.class_timing.from_hours
      #p e
      #p entry_hash
      #p check
      if check.include?(entry_hash)
       #p "AAAAAAAAAA"
      else
        e << entry_hash
        #p entries_array
      entries_array << e
      end
    end
    i = i + 1
  end

  #now group all the entries with same class_timings together and put them in the array
  #find the first element of this array and then compare it to the i + 1 element ka first array
  #  compare the class_timings of both these elements. if they are same then put them in a same array
  #p "LLLLLLLLLLLLL"
  p entries_array
  i = 0
  while i < entries_array.length
    #loop thru each element

    a = entries_array[i]
    p "AAAAAAAAA"

    #p entries_array
    p a
    first_element_a = a[0]
    arr = []
    isFound = true
    j = i + 1
    p i
    p j
    p  entries_array.length
    while isFound && j < entries_array.length
      p j
      b = entries_array[j]
      #p "BBBBBBBBB"
      #p a
      #p b
      first_element_b = b[0]
      if first_element_a["to_hours"] == first_element_b["to_hours"] && first_element_a["to_minutes"] == first_element_b["to_minutes"] && first_element_a["from_minutes"] == first_element_b["from_minutes"] && first_element_a["from_hours"] == first_element_b["from_hours"]


        arr.insert(0,b)


        p "ARRAY"
        p arr
        entries_array.delete(b)
        p entries_array
        #j = j - 1
      else
        entries_array.delete(a)
        p  entries_array
        arr.insert(0,a)
        p "ELSE ARRAY"
        p arr
        #entries_array[i] = arr
        entries_array.insert(i,arr)
        p  entries_array
        i = i + 1
        isFound = false
      end
    end
    if j == entries_array.length
      arr.insert(0,a)
      p "BREAK ARRAY"
      p arr
      entries_array[i] = arr
      break
    end


  end
  p "MMMMMMMMMMMM"
  p entries_array

  timetable_hash = {}
  entries_hash = {}
  entries_hash["group_code"] = group.group_code
  entries_hash["field_entries"] = field_entries.uniq
  entries_hash["entries"] = entries_array
  entries_hash["weekdays"] = weekdays.uniq
  entries_hash["batches"] = batches.uniq.compact
  timetable_hash["timetable"] = entries_hash
  p timetable_hash
  @timetable = ActiveSupport::JSON.encode(timetable_hash)

  render :json => @timetable

else
  timetable_hash = {}
  entries_hash = {}
  entries_hash["group_code"] = group.group_code
  entries_hash["field_entries"] = []
  entries_hash["entries"] = []
  entries_hash["weekdays"] = []
  entries_hash["batches"] = []
  timetable_hash["timetable"] = entries_hash
  p timetable_hash
  @timetable = ActiveSupport::JSON.encode(timetable_hash)

  render :json => @timetable
end

end

The code looks very bad with those ugly comments. I have to refactor them too. Along with adding the tests.