Override a form field value in rails form - ruby-on-rails-3

Note: I was overthinking things when I originally asked this question. The accepted answer is correct for the examples I gave - i.e. you can just pass :value to text_field, however I'd actually been having problems with date_select, which doesn't have a facility to override the value set.
As a result this has now been updated in Rails, so you can set :selected => a_date, and it will work as expected. This will be in Rails 4.
I have a model that can inherit its value from a parent model. It works something like this:
class User < ActiveRecord::Base
attr_accessible :field_name
belongs_to :company
def field_name
if self['field_name'].nil?
company['field_name']
else
self['field_name']
end
end
end
class Company < ActiveRecord::Base
attr_accessible :field_name
end
I then have a form to edit the User, but of course, if the User value is nil, then it populates the form with the value from Company, which is not what I want.
I would like to be able to override the value of the form field, so that if the User value is nil, then the value is empty.
Attempt 1
Ideally I'd be able to do:
<%= form_for #user do |f| %>
<%= f.text_field :field_name, #user['field_name'] %>
<% end %>
But that doesn't work, there doesn't seem to be a mechanism for providing an override value.
Attempt 2
So I thought about creating a second getter/setter:
def field_name_uninherited
self['field_name']
end
def field_name_uninherited=(value)
self['field_name']=value
end
Now I can use <%= f.text_field :field_name_uninherited %> and it works as expected - great! Except: when field_name is a date, or other type using multiparameter attributes, it results in this error:
1 error(s) on assignment of multiparameter attributes
I believe this is because it doesn't know that this is a date field, as it infers this from the database, and this field (with _uninherited suffix) is not in the database.
So I need some way to mark my additional method as the same type as the original database field.
A further note, the above examples (using field_name) are a simplified version. I'm actually using https://github.com/colinbm/inherits_values_from to handle the inheritance, but I don't think this is important to the question.
Obviously if there's a better way to accomplish the same goal, then I'm all ears.

So when it comes to displaying the value you for a user you want it to behave a bit differently?
What I'd do is use the :value option with your form field. That way you get to set the value like normal but choose what you want displayed in the form field.
<%= f.text_field :company, :value => user.field_name_uninherited %>

For what I understand, you want the user to put the field data and only if it's nil, populate that value with the parent (company) model. It seems to me before_save works perfectly, because it is called (as it name proposes) just before the save method is called on an ActiveRecord object.
Thus you can write this kind of callback:
class User < ActiveRecord::Base
attr_accessible :field_name
before_save :override_field
private
def override_field
if self.field_name.nil?
self.field_name = company.field_name
end
end
This way, you'll be only overriding the value if it's nil at the moment of saving, leaving that form field empty at the moment of creating a new element. Hope this works!

Related

Adding a column Migration with a default value in ruby on rails

I am using SQLite3 and I would like the following to work:
class AddNameToGoal < ActiveRecord::Migration
def change
add_column :goals, :goal_name, :text, default: goal.exercise.name
end
end
Or maybe this makes more sense as what I'm trying to do:
add_column :goals, :gname, :text, default: Goal.find(row_id).exercise.name
How do I get the above to work.
I doubt it will work as it is but that's what I want.
Specifically, The user is associated with an Exercise through the exercise_id column.
belongs_to :user
belongs_to :exercise
has_many :workouts, dependent: :destroy
(This is the model for Goal)...
I would like the user to be able to choose their own name for the Goal but I can give them the hint to name the goal after the Exercise's name and if they choose to leave it blank it will default to the exercise's name. More importantly this must happen on the SQL side so that later when I have a collection drop down which requires a name of the goal they will need a name which corresponds to the exercise.
<%= f.collection_select(:goal_id, #goals, :id,
:goal_name, :include_blank => "Please Select") %>
The Exercise Model is made in Rails to have
id, Name, other columns.
Exercise Model:
class Exercise < ActiveRecord::Base
has_one :goal
Is there a strategy by which that is possible.
Another option would be to help me find a strategy for active record so that I can do:
<%= f.collection_select(:goal_id, #goals, :id,
:goal_name, :include_blank => "Please Select") %>
with (something else to replace goal_name with Exercise.name like goal.exercise.name and goal.id and show only the ID.
Doing this when you define the column on the table is problematic. I definitely think doing it upon creation, in the model, is how you'd want to go.
You might also check out the default_value_for gem and see if it helps.
Hi so in case any body ever wants to do this:
I figured out a strategy...
This is perfect for anything that has an instance of name in terms of the user.
For example, you have something that tracks your Car and you want to give that Car by default the name which comes from that Car's Make_and_Model (model) in Rails.
Naturally when someone says they have a new "Honda Accord" then they get to have that name, but if they ever want to change it to "Lucy" because her name is Lucy and you better treat her with the respect she deserves!, then this gives you the option to do that.
You do not want to change the name for that whole Make&Model you only want to change the name for that specific car.make_and_model which belongs_to User.
If you are wanting something to have a name that defaults to another name but allows the user to change that. Do that on the model level... by setting a before_save method inside the model... like so:
before_save :default_values
def default_values
self.goal_name = self.exercise.name if self.goal_name.nil?
end

Rails 3 How to access user data from user_id column in belongs_to :user association

I am trying to create an activity feed with the most recent activities from my TrainingSession model.
class User
has_many :training_sessions
end
class TrainingSession
belongs_to :user
end
The problem is that I am trying to access a user's data in the view page (mainly the user's name) by instantiating an object from the TrainingSessions database table, as shown below:
<% #training_sessions.each do |training_session| %>
<%= training_session.user_id %>
The problem is that, although I successfully get the user's id, I cannot call, for example:
training_session.user_id.name
... otherwise I get the NoMethodError shown below:
undefined method `first_name' for 2:Fixnum
so my question is ... how can I access the user's data from the TrainingSession's object?
any help would be much appreciated. Pretty stumped on this one.
The reason that you get a "undefined method `name' for nil:NilClass"-error is that some training sessions do not belong to a user. The solution is to cleanup your database:
DELETE FROM training_sessions WHERE user_id IS NULL
If it is expected behavior to have training sessions that don't belong to a user, you have to check that the user is not nil in your loop:
<% #training_sessions.each do |training_session| %>
<% unless training_session.user.nil? %>
<%= training_session.user.name %>
<% end %>
<% end %>
First of all, you need to rename your model name (TreningSessions) into singular name (TreningSession). That's the convention rails uses. Rename only model, leave has_many without change.
Now the user association,you should call it via user object. user_id is just a attribute that represents field in database and it's value, while user is an association object. Try this:
training_session.user.name
More on ActiveRecord relations
Here is what I ended up doing, creating a local user variable containing the user_id and using that variable with the find method on the user model to instantiate an instance variable #training_session_user in my controller, like the following:
#training_sessions.each do |training_session|
user = training_session.user_id
#training_session_user = User.find(user)
end
then I call this in my view:
#training_session_user.first_name
and it retrieves the name with no errors.
If anyone has a better solution please feel free, but I will mark this as correct for now.

Active record::relation instead of the object's class, when using where method with Rails 3

I am trying to get the attributes of the objects after calling a .where query. The query is the following:
#matchers = TutoringSession.where(:begin_time_hour => 21).limit(5)
I get an array of tutoring sessions as a result. But I would like to be able to return only specific attributes of each of the matching tutoring sessions. So I have the following code in my view:
#matchers.each do |matcher|
matcher.begin_time_hour
end
Instead of listing each of matcher's begin_time_hour attributes, it all of the attributes for each matcher object. I have experimented with this block trying "puts matchers.begin_time_hour," and have also tried using partials to solve this problem, however I keep running into issues. If I ask #matcher.class, it says, it is ActiveRecord::Relation object. I thought it would be a TutoringSession object.
Here are my models, in case this helps.
require 'date'
class TutoringSession < ActiveRecord::Base
belongs_to :refugee
belongs_to :student
before_save :set_day_and_time_available, :set_time_available_hour_and_day
attr_accessor :begin_time, :book_level, :time_open
attr_accessible :time_open, :day_open, :tutoring_sessions_attributes, :page_begin, :begin_time
end
and my other class is the following
require 'date'
require 'digest'
class Refugee < ActiveRecord::Base
has_many :tutoring_sessions
has_many :students, :through => :tutoring_sessions
accepts_nested_attributes_for :tutoring_sessions, :allow_destroy => true
attr_accessible :name, :email, :cell_number, :password, :password_confirmation, :day_open, :time_open, :tutoring_sessions_attributes
end
Please let me know if you need more info. Thanks for the help!
It looks like you're not outputting anything to the view. By calling
#matchers.each do |matcher|
matcher.begin_time_hour
end
you get the result from running the loop, which is the relation, instead of the data. You are accessing begin_time_hour, but you aren't doing anything with it. You'd need something more like this to display the begin_time_hour fields.
<% #matcher.each do |matcher| %>
<%= matcher.begin_time_hour %>
<% end %>
By the way, #matchers should be an ActiveRecord::Relation object, a representation of the sql query that will be generated from the where and limit clauses. Calling all on the relation with make it an array of TutoringSession objects
#matchers = TutoringSession.where(:begin_time_hour => 21).limit(5).all
Calling each implicitly runs the query and iterates over the TutoringSession objects, so you shouldn't need to worry about that though.

Rails syntax Passing POST parameters from a form to a controller

I'm new to Rails (and fairly new to programming in general) and I am building a web app for myself as a way to learn. Right now I am modifying scaffolded forms and such.
My question is with the "create" method in one of my controllers. There are two entities I am concerned with: the User table and the Habit table. I created a dropdown box in the _form partial for the Habit views to allow a person to select a user from a list of all available when creating a habit as below
<%= collection_select :user, :id, #users, :id, :first_name %>
The habit controller, of course, has
def new
#users = User.all
...
end
This works fine, and when the form submits it posts two hashes of parameters :habit and :user. Now, when I want to process the form input in the create method, I'm not sure how to use the syntax correctly and assign the user_id to the newly create habit. What I WANT to do is something like this
def create
#habit = Habit.new(params[:habit], params[:user])
end
This, of course, is improper syntax.
def create
#habit = Habit.new(params[:habit])
end
assigns the params from the :habit hash correctly, but then the user_id is left unset.
What works is the following, but the code is very lengthy, assigning each value manually.
def create
#habit = Habit.new(:user_id => params[:user][:id],
:description => params[:habit][:description],
:habit_method => params[:habit][:habit_method],
:time_reqd => params[:habit][:time_reqd],
:will_reqd => params[:habit][:will_reqd],
:active => params[:habit][:active])
end
So my question is, when dealing with a form that posts data in multiple hashes, what is the proper way to pass those parameters into some method in a controller?
So my question is, when dealing with a form that posts data in multiple hashes, what is the proper way to pass those parameters into some method in a controller?
Instead of saying Habit.new( <lots of stuff> ), just use Habit.new(params[:habit]). Rails will try to assign each key in the hash (in this case, the params[:habit] hash's keys) to a matching value on the object.
Thus, if params[:habit] has a :description key, it will be assigned to a field called description on your model. This is called mass assignment and is quite handy.
Now you can just do:
#habit = Habit.new(params[:habit])
#habit.user_id = params[:user][:id]
You may want to read the RoR Getting Started Guide, like this section, for more similarly handy features of Rails.
Change
<%= collection_select  :user, :id, #users, :id, :first_name %>
To
<%= collection_select  :habit, :user_id, #users, :id, :first_name %>
The existing scaffold code should just work after that
Alternate
<%= f.select :user_id, #users, :id, :first_name %>

Accept terms of use rails

What is the best way to add a check for accepting terms of use in a rails app?
I can't seem to get validates_acceptance_of working quite right. I added a bool to my user model (was that necessary?). And then have a checkbox that returns either true/false.
I feel like I'm just making a silly little mistake. Any ideas?
In your model,
validates_acceptance_of :terms
If you're using attr_accessible in your model then make sure you also add,
attr_accessible :terms
In your view,
<%= form_for #user do |f| %>
...
<%= f.check_box :terms %>
...
<% end %>
There is no need for an extra column in the users table unless you plan on denying access to users who have not accepted the terms of service, which won't exist since they can't complete registration in the first place.
This is a working Rails 4 solution:
Terms of service doesn't need to be a column in the database
Form
= f.check_box :terms_of_service
models/user.rb
validates :terms_of_service, acceptance: true
And most important, devise will sanitize your parameters and terms of service will be removed from the submitted params. So:
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:full_name,
:email, :password, :password_confirmation, :terms_of_service)
end
end
end
This is a working solution for Rails-6.1 (I18n) + Devise-4.8.0 + SimpleForm. No need to add a column in the "users" table in DB.
View
<% label_str = t('read_html', mylink: link_to(t('terms'), '/a/b/c')) %>
<%= f.input :terms_of_service, label: label_str, as: :boolean, checked: false %>
Or, if you use Indo-European languages only, you can make it a little more simple, like:
label_str = (t('agree_html')+link_to(t('terms'), '/a/b/c')+'.').html_safe
/app/models/user.rb
attr_accessor :terms_of_service
validates_acceptance_of :terms_of_service, on: :create
validates_presence_of :terms_of_service, on: :create
/app/controllers/application_controller.rb
Devise::ParameterSanitizer::DEFAULT_PERMITTED_ATTRIBUTES[:sign_up] << :terms_of_service
# see /vendor/bundle/ruby/*/gems/devise-*/lib/devise/parameter_sanitizer.rb
Explanation
In the User model, on: create guarantees it is read only in creation. If you need to reevaluate the condition in updating, too, specify it accordingly, like on: %i(create update).
In the User model, I add validates_presence_of to play safe. The reason is, validates_acceptance_of will not be executed when the parameter terms_of_service is nil, in which case validates_presence_of will catch it and set an error. Admittedly, if the data are always submitted via the web-interface you have built AND your implementation is working perfectly, the value should be always either true or false and never be nil. So, validates_presence_of should not be necessary in this sense. It does no harm, though (except you'd need to be a little careful in manual user creation, bypassing the web-interface, such as from the Console).
The last one is neccesary for use with Devise for the same reason as in the answer by #vladCovaliov; that is, to prevent Devise from sanitizing your custom parameter, which is not a column in the database table. The one-liner in the example above can be stated in any files as long as you are sure it is read at the run-time and after Devise Ruby code. application_controller.rb is one of the sure places (though I guess there is a better-fitting place). Make sure the sentence is put out of the class ApplicationController block.