Using STI path with same controller - ruby-on-rails

I am using STI and am wondering, do I have to have a separate controller for each model? I have a situation where I only use the create and edit actions for one model in the STI relationship, but I get an 'undefined method' error if I try to do a form for. More specifically, I have two models that inherit from List:
class RegularList < List
class OtherList < List
and I have a lists controller that handles these actions, but I only create new models with RegularList using forms. i.e. the only situation where I use a form_for to create a new List object is with RegularList. What I would like to do is something like:
class ListsController < ApplicationController
def new
#list = RegularList.new
end
otherwise the route for creating a new list looks like regular_list/new but I would like it to just be list/new. Thoughts?
EDIT: The problem is when I use the above code, I get an 'undefined method' error. My view looks like this:
...
So it seems that there is some problem with using a RegularList object in the Lists controller and this is the main problem I am trying to address. Sorry, I realize that was not the clearest explanation.

I know this is kind of late, but maybe this will be helpful for other people. You want to use the becomes method. Let's say you are editing #list which is an instance of RegularList. Then do
form_for #list.becomes(List)

I am using STI and am wondering, do I have to have a separate controller for each model?
No, you don't.

Related

Rails how to use 2 of the same slugs

I am using slugs in my project to give my params an other name but I have two params called: "how-does-it-work".
(.../investor/how-does-it-work)
(.../customer/how-does-it-work)
I would like to use the slugs as how they are currently set.
Is there a way to do that?
Create two distinct routes/controllers, and simply query the corresponding ActiveRecord model in the show action. Assuming there is a slug field on your models:
Rails.application.routes.draw do
resources :customers
resources :investors
end
class CustomersController < ApplicationController
def show
#customer = Customer.find_by(slug: params[:id])
end
end
class InvestorsController < ApplicationController
def show
#investor= Investor.find_by(slug: params[:id])
end
end
This is probably the most conventional way to solve this problem in Rails. If you are using the friendly_id gem, the same approach more or less applies, except for maybe the query itself.
Hope this helps.
So, is /investor/ and /customer/ both parts of the slug?
If that's the case, you can split the string, and do a search based on the "how-does-it-work" in the grouping of "investor" or "customer".
If investor and customer are both parts of the routes, you shouldn't have a difficult time there, because they're pointing to two different controller methods. You should be able to write a search based on each of those methods that correspond to the data. If the data is the same, all your doing is pointing the controller to the correct model data with the correct params.
If you're using friendlyId, it usually has built in candidate matching. Also, if you're meaning to match multiple pages to the same slug (which I've done in the past), you can display a results page if you'd like too, by rendering based on the quantity of results.

Rails, add data to association table by adding custom method to controller

I am new to rails and I feel this question should have an easy answer but I can't figure it out.
in rails I have two tables USERS and INFOS they have many to many relationship and there is a third table INFOS_USERS but it doesn't have the model.
my problem is I want to add to this association table from Info view, but I have no method to call in the controller!
should I create a new model and controller?
is it possible to add a custom methods to controller?
Thanks
edit: I can write to table from controller but problem is I don't have any controller for the association table so I don't have any method to call from view.
Yes, you can add custom methods (actions) to a controller
Your routes might look like:
resources :users do
members do
get 'infos' # => /users/:id/infos
post 'add_info' # => /users/:id/infos/add_info
end
end
In this case it might make sense to use nested routes, which means you would have a different controller for the 'infos' - checkout the rails guides http://guides.rubyonrails.org/routing.html#nested-resources
for adding #user to #info:
#info.users << #user
if you need create new User from association:
#info.users.build
and you don't need any extra Controllers or Models

Rails 4.0 multiple models and one controller

I'm in the process of learning Ruby on Rails, and now I have created the mobile version of my application.
I created the relation between models ans controller is one-one. Now I want to make changes to manage three models from one controller. I have read and watch videos a lot about how to do this but, it doesn't work when I try to do it in my application.
Models:
class Subject < ActiveRecord::Base
has_many :pages
class Page < ActiveRecord::Base
belongs_to :subject
has_many :sections
class Section < ActiveRecord::Base
belongs_to :page
Controller:
class SubjectsController < ApplicationController
has_mobile_fu
layout "admin"
before_action :confirm_logged_in
def index
#subjects = Subject.newest_first
#pages = #subjects.pages.sorted
end
This is the error:
NoMethodError (undefined method pages' for # <ActiveRecord::Relation::ActiveRecord_Relation_Subject:0x007fbbf3c9b218>):
app/controllers/subjects_controller.rb:10:inindex'
The application works well if I keep each model managed by its controller. The problem started now that I want to control multiple models from one controller.
Can definitely use multiple models in a single controller. The issue here is you're calling a method that doesnt exist for the active record relation.
An active record relation is typically a collection of returned objects from a query using active record. So the newest_first is returning multiple, not just one. If you want to get all pages for the subjects and sort them, you can do this:
#subjects = Subject.newest_first
#pages = #subjects.map(&:pages).flatten.sort { |a, b| a.title <=> b.title }
Can switch the attribute on which you wish to sort by. The map function goes through each one, and returns the object of which i passed in the symbol. It's a shortcut for:
#subjects.map { |subject| subject.pages }
The flatten then takes that array of active record relations and flattens it into a single array. I then just use the array sort.
Edit Here's a way you can do it using the database:
#subjects = Subject.newest_first
#pages = Page.where.not(:subject_id => nil).order(:title)
MVC
Something else you'll benefit from is to look at the MVC Programming Pattern:
Rails is famous for its strict coherence to the Model-View-Controller pattern, as it works like this:
You send a request to your app
Rails "routes" your request to a specific controller / action
The controller will then collate data from your Models
The controller will then render a view to display this data
The relationship between models and controllers is exclusive; meaning you don't have to call certain models from a controller, etc.
So the basic answer is no, you don't need to call a single model from a controller. However, you do need to ensure you have the correct model associations set up, as per the explanation below:
Associations
The caveat here, is that since Ruby is object-orientated (and Rails, by virtue of being built on Ruby, also being so), it's generally considered best practice to build your application around objects
"Objects" are basically elaborate variables (constructed from your Model classes), but the pattern behind making OOP work properly is super important - everything from Rails' routes to your controller actions are designed to be object-ORIENTATED
Each time you initiate an instance of a Model, Rails is actually building an object for you to use. This object allows you to call / use a series of attributes / methods for the object, allowing you to create the experience you require with Rails
--
The bottom line -
I would highly recommend examining the ActiveRecord Associations in your models (which will determine whether you need to call a single model or not):
#app/controllers/subjects_controller.rb
Class SubjectsController < ApplicationController
def index
#subjects = Subject.newest_first #-> good use of OOP
#posts = # this is where your error occurs (`.posts` is only an attribute of each `Subject` object instance, which is fixed using the accepted answer)
end
end
Hopefully this gives you some more ideas about how to construct Rails applications

Nested form to invoke custom factory method instead of new()

This problem is related to this question: Hashing an IP for saving
I have a model called Post. I have to pass in the IP address during post creation. I was told not to override the initialize(). So I used a factory method as suggested here: how to override new method for a rails model:
#Post.rb model
def self.new_with_ip(ip, attributes={})
self.new(attributes['one_day_id'] = do_some_conversion_on(ip))
end
However this does not get invoked, because Post is nested within a Discussion, and the nested form will not call this factory method. How can I make the form to invoke this instead of the traditional Post.new()?
If this is being passed through as a nested attribute of a form then you would have to override the posts_attributes= method of the Discussion model:
def posts_attributes=(attribute_sets)
attribute_sets.each do |attributes|
Post.new_with_ip(ip_goes_here, attributes)
end
end
Of course you're going to need to modify that a little if you're going to be getting nested posts in an update kind of fashion, as you'll want to update existing posts rather than creating new ones. Sounds like a good exercise in learning :)

Relation Between 2 Controllers

How can I define a relationship between two controllers. I have one controller called rides and another called registrant. Is there anyway I can access the registrant database from within the rides controller? I was thinking
#registrant = Registrant.find(:first)
from within rides, but that didn't work.
Any suggestions?
Thanks
You can access your registrant model from your rides controller just like accessing it from any other controller. What do you mean by Registrant.find(:first) not working?
Now, if there's a relationship (or association as it's normally called) between your rides model and registrant model (like a has_many association), you can use nested resources to nest one controller in another.
Check out the Action Controller Overview and Rails Routing from the Outside In guides and think about picking up a good book on Rails like Agile Web Development with Rails.
If you have defined models: ride and registrant (or more general user) then you can setup a before_filter on the rides controller:
before_filter :get_user
def get_user
#user = User.find(:first, :conditions => %Q(userid = "#{params[:user_id]}"))
end
This would fetch the the user with user_id passed in as a parameter before the controller generates the view.
Yes, that should work. To get the terminology right, you are accessing the Registrant model from the RidesController. They should both be in the same database, but in separate tables.
Please post the error message you are getting.

Resources