I have to entities, project and words so far every time when I need to check if project 1 had some words, I was passing the project_id to words model.
class Words
def word_exist? project_id
project = Project.find(project_id)
words = project.words.exists?(self.id)
end
end
Because in my controller I already set the project, do I need to do it in my model as well? Or I can pass the instance variable of the project to model.
You can pass project itself as parameter:
def word_exist?(project)
keywords = project.keywords.exists?(id)
end
and in controller:
#project = Project.find(params[:id]) # example
words = Words.new
exists = words.word_exist?(#project)
You'll be best using an instance method to access the required data:
#app/models/project.rb
class Project < ActiveRecord::Base
has_many :keywords
def word_exist?
self.keywords.exists?(self.id)
end
end
This will be called like so:
#project = Project.find params[:id]
#project.word_exist?
Because it's an instance method on your model, you'll already have the object's data, which the instance method can then reference with self
If you wanted to find a specific word, you'll be able to use arguments on your instance method:
#app/models/project.rb
class Project < ActiveRecord::Base
has_many :keywords
def word_exist?(word)
self.keywords.exists?(keyword: word)
end
end
#project = Project.find params[:id]
#project.word_exist?("hello")
Related
Example code:
#model
class Profile < AR:Base
has_many :projects
end
#controller
class ProfilesController < AC
def show
#user = Profile.find(params[:id])
end
end
#view
#profile.projects.each do |project|
= render something
end
Any user can view any profile, but projects should be filtered by visibility (like public/private projects).
I'm concerning to add one more ivar because it violates Sandi Metz's rule
Controllers can instantiate only one object. Therefore, views can only
know about one instance variable and views should only send messages
to that object (#object.collaborator.value is not allowed).
The only way I see it now is to introduce another class (facade) to do this things, like:
class ProfilePresenter
def initialize(profile, current_user)
#profile = profile
#current_user
end
def visible_profiles
ProjectPolicy::Scope.new(current_user, profile.projects).resolve
end
end
Am I missing something?
How would one achieve it (resolving association scopes) using Pundit?
In case we will need pagination for projects within profile view - what approach to choose?
I have a rails 4 app.
When creating permitted params in a controller where there is a belongs_to association with another model, do you need to include the foreign key in the permitted params, so that it can be updated when the record is saved, or is that automatic?
It's NOT automatic.
In Rails 4, you have to permit the attribute in order to be able to mass assign its value. The foreign key of the other model is an attribute of your current model that you're trying to update. Without permitting that, you can't update it's value.
The foreign key is not automatic, the associated object is:
This means the following is true:
#app/controllers/your_controller.rb
class YourController < ApplicationController
def create
#item = Item.new new_params
#associated = Associated.find x
#item.associated = #associated #-> this always works & will save
#item.save
end
private
def new_params
params.require(:item).permit(:name, :etc) #-> foreign_key would have to be explicitly defined here if associated_id was passed from a form
end
end
This should give you some perspective on what you can do with your objects.
Update
If you want to assign a post to the current user each time, you'd be able to use the following:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def create
#post = Post.new post_params
#post.user = current_user # -> however you identify the user
#post.save
end
end
Trying to figure our how to set up associations in form.
I have 3 models:
class Request < ActiveRecord::Base
has many :answers
has many :users, through: :answers
end
class Answer < ActiveRecord::Base
belongs to :user
belongs to :request
end
class User < ActiveRecord::Base
has many :answers
has many :requests, through: :answers
end
I am trying to figure out: how to have a User link to Answer#new from Request#Show, and then create an Answer record passing in the Request#Show request_id from the previous page - creating an association between the User's Answer and the Request he was viewing.
My method of doing this now is: I flash the request_id value on Request#Show, and then when a User links to Answer#new, it passes the flashed value into a hidden form tag on Answer#new. This does not seem like the best way to do this.
Any thoughts?
Kudos for the creative approach using flash, however your right there is an easy way. You can pass parameters much between controllers just like passing parameters between methods using the route names.
I didn't quite follow what it was you were trying to achieve in this case but it looks like this blog entry here should get you started..
https://agilewarrior.wordpress.com/2013/08/31/how-to-pass-parameters-as-part-of-the-url-in-rails/
Good luck!
User link to Answer#new from Request#Show
This can be achieved with either sessions or nested resources (or both!). Let me explain:
I would definitely add a nested resource to your requests routes:
#config/routes.rb
resources :requests do
resources :answers, only: [:new, :create] #-> url.com/requests/:request_id/answers [POST]
end
This gives you the ability to call a "nested" route (IE one which sends data to a child controller, and requires "parent" data to be appended to the request).
In your case, you want to create an answer for a request. The most efficient way is to use a routing structure as above; this will allow you to use the following controller method:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
#request = Request.find params[:request_id]
#answer = #request.answers.new
end
def create
#request = Request.find params[:request_id]
#answer = #request.answers.new answer_params
#answer.save
end
private
def answer_params
params.require(:answer).permit(:title, :body)
end
end
The above gives you the ability to create an answer by passing the request_id to the nested route. You must note the corresponding route will require a POST method in order to work.
You don't need the new method. If you wanted it, it can easily be handled with the above structure.
Passing the user is a little more tricky.
You can either use the routes, or set a session.
I would personally set a session (it's cleaner):
#app/controllers/requests_controller.rb
class RequestsController < ApplicationController
def show
session[:user_id] = #user.id #-> I don't know how you populate #user
end
end
This will give you the ability to access this session here:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
user = User.find session[:user_id]
end
end
#app/views/requests/show.html.erb
<%= link_to "New Answer", request_new_answer_path(request) %>
--
If you're using Devise, the user object should be available in the current_user object (which means you don't have to set session[:user_id]):
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
## current_user available here if using devise
end
end
To assign a #user to the new answer record, just do this in answers#create:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
...
def create
#request = Request.find params[:request_id]
#answer = #request.answers.new answer_params
#answer.user = current_user
#answer.save
end
end
Something like this worked for me:
I have two models (Formula and FormulaMaterial)
Formula has_many FormulaMaterials, which belongs to Formula
My Formula controller sets #formula like so:
#formula = Formula.find(params[:id])
I list my Formula Materials in my Formula show.html.erb by declaring it in my Formula controller like so:
#formula_materials = FormulaMaterial.where(:formula_id => #formula)
When I want to add a new FormulaMaterial to my Formula, the "New Formula Material" button in my show.html.erb file looks like this:
<%= link_to 'Add Material To Formula', new_formula_material_path(:formula_id => #formula), class: "btn btn-success" %>
In the "new_..._path" I set the associated id to the #formula variable. When it passes through to the new.html.erb for my FormulaMaterial, my URL looks like so:
http://localhost:3000/formula_materials/new?formula_id=2
In my FormulaMaterial new.html.erb file, I created a hidden_field that sets the value of the association by using "params" to access the formula_id in the URL like so:
params[:formula_id] %>
I am not sure if this is the best way to do this, but this way has allowed me to pass through the view id from the previous page as a hidden, associated and set field in the form every time.
Hope this helps!
For example:
Environment:
Ruby 1.9.2
Rails 3.0.5
Mongoid
I have a Survey model and embeds many Questions in it. Now I want to define a function "publish" in surveys_controller.rb which can create model dynamically according to Survey's records (i.e., objects):
#surveys_controller.rb
def publish
#survey = Survey.find(params[:id])
#questions = Survey.questions
... how to build a model? ...
#questions.each do |question|
... and then how to add fields (named with question.title) and define type (named with question.type) ...
end
end
i'm assuming your models looks like this:
class Survey < ActiveRecord::Base
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :survey
end
you can create a question associated to a survey this way:
def publish
# here, we instantiate an object by loading
# a record from the Survey model
#survey = Survey.find( params[:id] )
# here, we instantiate an object of class Question,
# associated to our object from class Survey.
# the argument passed to the build() method
# depends on your form.
#question = #survey.questions.build( params[:question] )
# when we save our #survey, its associated #question
# will be saved as well
#survey.save
end
you may want to add validates_associated :questions to your Survey model, to make sure your questions are valid when saved.
More information is available in Ruby on rails guides and in Rails API doc
Most Rails tutorials show how to populate a model class via the params hash like so:
class UsersController < ApplicationController
def create
#user = User.create(params[:user])
# more logic for saving user / redirecting / etc.
end
end
This works great if all the attributes in your model are supposed to be strings. However, what happens if some of the attributes are supposed to be ints or dates or some other type?
For instance, let's say the User class looks like this
class User < ActiveRecord::Base
attr_accessible :email, :employment_start_date, :gross_monthly_income
end
The :email attribute should be a string, the :employment_start_date attribute should be a date, and the :gross_monthly_income should be a decimal. In order for these attributes to be of the correct type, do I need to change my controller action to look something like this instead?
class UsersController < ApplicationController
def create
#user = User.new
#user.email = params[:user][:email]
#user.employment_start_date = params[:user][:employment_start_date].convert_to_date
#user.gross_monthly_income = params[:user][:gross_monthly_income].convert_to_decimal
# more logic for saving user / redirecting / etc.
end
end
According to the ActiveRecord documentation, the attributes should automatically be typecasted based on the column types in the database.
I would actually add a before_save callback in your users model to make sure that the values you want are in the correct format i.e.:
class User < ActiveRecord::Base
before_save :convert_values
#...
def convert_values
gross_monthly_income = convert_to_decimal(gross_monthly_income)
#and more conversions
end
end
So you can just call User.new(params[:user]) in your controller, which follows the motto "Keep your controllers skinny"