Parameters/methods in models in rails - ruby-on-rails

I've added an extra method to my model in rails, let's just say it is:
def subscribed
true
end
If I create a form with:
<%= f.check_box :subscribed %>
it correctly prints a ticked checkbox, but on submission of the form it says
unknown attribute: subscribed
What am I doing wrong?

Add an attribute accessor like this:
class MyModel
attr_accessor :subscribed
end

Related

Automatically creating a record for the user in rails

I am working on status feature for my rails app. I have everything set up and it works perfectly fine. For the interface, I am using the best_in_place gem to allow users to edit in place for the status. The code is given below.
<% if #scoreboard.status.content.present? %>
<div id="statuscontent">
<%= render 'statusedit' %>
</div>
<% else %>
<%= render 'statusupdate' %>
<% end %>
The statusedit partial contains the best_in_place code and the statusupdate partial contains the normal form for creating the status. Codes for both partials are given below.
statusedit
<%= best_in_place [#scoreboard, #status], :content, place_holder: "Enter a status", as: "textarea" %>
<%= link_to "delete", [#scoreboard, #status], method: :delete, data: {confirm: "Are you sure"} %>
statusupdate
<%= form_for [#scoreboard, #status] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content, class: "form-control", id:"status" %>
<%= f.submit "Post", class: "btn btn-primary", id: "status-button" %>
<% end %>
The above code is working perfectly fine, however, I am struggling to set up the interface I want. The problem is that best_in_place editing only works if the status has been created before. Therefore, I have to create the status using the form and then I can use in place editing. However, I want the interface such that users can use in place editing without the need of physically creating a status first. Is there a way to create a default status for every user? Or a before_update function that maybe creates a status before the user tries to update.
I tried using Ajax for creating status to achieve the interface but it didn't work as intended. Also, I feel there must be a simpler solution to this problem. I tried setting :default => "please upload status" in the database but that doesn't actually create a default record. It acts like a placeholder but you still have to click post for the record to be created. Is there a way to somehow automatically create a default status for each user or set a value in the database. I have read a few stack-overflow posts on this but nothing really points to the right direction. There must be a simpler way of doing this in rails. Any documentation or suggestions would be a great help. As always, any help is always greatly appreciated!! Thanks
I have added the relevant migration and model files.
status model
class Status < ActiveRecord::Base
belongs_to :scoreboard
end
scoreboard model
class Scoreboard < ActiveRecord::Base
has_one :status
end
Status migration file
class CreateStatuses < ActiveRecord::Migration
def change
create_table :statuses do |t|
t.text :content
t.references :scoreboard, index: true
t.timestamps null: false
end
add_foreign_key :statuses, :scoreboards
end
end
Answer for your questions.
Is there a way to create a default status for every user ?
Yes
You could create a default record in database after change_column.
def up
change_column :users, :admin, :boolean, default: false # I'm assuming you are saving default as false in users table but you could change accordingly all thing.
end
before_update function that maybe creates a status before the user tries to update.
instead of before_update one could use after_save callback.
As mentioned in official Document
after_save runs both on create and update, but always after the more
specific callbacks after_create and after_update, no matter the order
in which the macro calls were executed.
And lastly
There must be a simpler way of doing this in rails
Use ActiveRecord Migrations.
Apologies if this answer is not specific; you wrote a lot.
You're best setting a default value for an attribute at the database level.
Yes, you can use the before_create callback:
#app/models/scoreboard.rb
class Scoreboard < ActiveRecord::Base
before_create :set_default_status
private
def set_default_status
self[:status] = "default"
end
end
The problem with this is that it adds application-level logic which can be handled by the db:
$ rails g migration CreateStatusDefault
# db/migrate/create_status_default________.rb
class CreateStatusDefault < ActiveRecord::Migration
def change
change_column :scoreboards, :status, default: "Default"
end
end
$ rake db:migrate
Creating a default value in the db basically means that unless you send a value from your model at create / update, you're going to get the default.
The beauty of setting it in the DB is that your Rails app has absolutely zero work to do for it, making it much more efficient.
--
I subsequently found that your status "object" is actually a model in itself, which is probably where your issues are coming from.
If that is the case, you'll need to build the associative object before you create your main/parent one:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :statuses
before_create -> (model) { model.statuses.build }
end
We use this to build a profile for each User who signs up to our applications; it creates a "blank" associative record for your parent model.
This is something which would give you a "blank" status object, but as you've not posted your code, I really don't know.
A good tip with SO is code > text.
If in doubt, post code.
Is there a way to create a default status for every user?
Yep you can write it yourself. Something like this ...
User < ActiveRecord::Base
has_many :statuses
after_create :create_first_status
def create_first_status
self.statuses.create some_status_attribute: "some value"
end
end

simple_form rails 4, set automatic association

is a little project and I try to associate patient model with consultations. one patient has_many :consultations, in my form I have:
<%= f.association :patient %>
I pass the id parameter from the patient to the action 'new' in this way:
<%= link_to new_consultum_path(:id => #patient.id) %>
And in the view I have:
How can I make that the f.association field take the correspondent patient_id automatically?
How can I be sure that the patient_id is the current patient?
If I want to hide this field is that ok if I put
instead of
Is a better way to do this?
And why in the view shows me # patient:0x007f4e7c32cbd0 ?
thanks for your help.
And why in the view shows me # patient:0x007f4e7c32cbd0
This is a Patient object.
It means you need to call an attribute of this object - EG #patient.name.
--
f.association field take the correspondent patient_id automatically
This might help:
It looks like Organization model doesn't have any of these fields: [
:to_label, :name, :title, :to_s ] so SimpleForm can't detect a default
label and value methods for collection. I think you should pass it
manually.
#app/models/patient.rb
class Patient < ActiveRecord::Base
def to_label
"#{name}"
end
end
Apparently, you need to have either title, name or to_label methods in your model in order for f.association to populate the data.
-
How can I be sure that the patient_id is the current patient?
If you're having to verify this, it suggests inconsistencies with your code's structure. If you need the patient_id to be set as the current patient, surely you could set it in the controller:
#app/controllers/consultations_controller.rb
class ConultationsController < ApplicationController
def create
#consultation = Constultation.new
#consultation.patient = current_patient
#consultation.save
end
end
I can provide more context if required.
You want to associate consultations to patients using fields_for, which is similar to form_for, but does not build the form tags.
It you start with your patient object, you can iterate through the consultation associations binding it to form fields as you go.
it would look something like this
<%= form_for #patient do |patient_form| %>
<% patient_form.text_field :any_attribute_on_patient %>
<% #patient.consultations.each do |consultation| %>
<%= patient_form.fields_for consultation do |consultation_fields| %>
<% consultation_fields.text_field :any_attribute_on_consulatation %>
<% end %>
<% end %>
<% end %>
Sorry, the code may not be exactly right.
Check out the docs for field_for here
You will also have to set accepts_nested_attributes_for consultations on patient. When you set accepts_nested_forms_for, Rails will automatically update the associated consultations object to the patient and save any edits you have made. You DEFINITELY want to use accepts_nested_attributes_for most nested form handling of this type.

Rails validation and related tables issue

There are many students and each student belongs to a program.
I have a controller:
def edit
#student = Student.find(params[:id])
#programs = Program.all
end
Then I have an edit form (simplified):
<%= f.label(:program_id, "Program") %>
<%= f.select(:program_id, #programs.collect {|c| [c.name, c.id]}) %>
Finally, I have a model for the student:
class Student < ActiveRecord::Base
belongs_to :program
validates :first_name, presence: true
end
I can update a student, as long as I input a first name. If I leave first name blank, I get an error undefined method 'collect' for nil:NilClass
However, if I change the form just a little bit, everything works. Like so:
<%= f.label(:program_id, "Program") %>
<%= f.select(:program_id, Program.all.collect {|c| [c.name, c.id]}) %>
Notice the Programs.all in there. Instead of in the controller.
What gives? I'd really like to define #programs in the controller. Am I missing something obvious?
Thanks!
P.S. If I take the validation rule out of the model, everything works again--so I'm pretty sure the validation is at the heart of the matter.
Do you have an update method in your controller? My guess is in the update action you have an if statement that says if the save fails, render the edit template again. If you are rendering the edit template from the update method, the view no longer knows about #programs, so you would need to redefine again in the update method, or create a before_action.

Disable Humanizer in ActiveAdmin, Rails 4

I have simple app where users can register when they pass Human validation.
For that my setup is like this:
Model:
include Humanizer
attr_accessor :bypass_humanizer
require_human_on :create, :unless => :bypass_humanizer
View:
<%= f.label :humanizer_answer, #advertisement.humanizer_question %>
<%= f.hidden_field :humanizer_question_id %>
This far everything works.
Also I want to allow admin user to register new users in ActiveAdmin panel.
As we now ActiveAdmin uses controller actions if we don't override them.
Based on Humanizer documentation I have to set bypass_humanizer to true when I want to disable Human validation.
So I am overriding create action like this:
controller do
def create
bypass_humanizer = true
super
end
end
But it don't want to work as expected.
Any help on this ?
Thanks in advance for your time.
ActiveAdmin.register Model do
before_create do |model|
model.bypass_humanizer = true
end
end
Or you can place a hidden input with name bypass_humanizer and value true in the form.

Edit a serialized hash in a form?

I'm serializing a hash that is stored in a settings field in a table, and would like to be able to edit that hash in a form field.
class Template < ActiveRecord::Base
serialize :settings
end
But I just do <%= f.text_area :settings %> then the text area just shows the serialized data instead of the hash.
How can I get the hash to show in the text area?
Maybe setting up another accessor for your model would work.
class Template < ActiveRecord::Base
serialize :settings
attr_accessor :settings_edit
before_save :handle_settings_edit, :if => lambda {|template| template.settings_edit.present? }
def settings_edit
read_attribute(:settings).inspect # should display your hash like you want
end
protected
def handle_settings_edit
# You may want to perform eval in your validations instead of in a
# before_save callback, so that you can show errors on your form.
begin
self.settings = eval(settings_edit)
rescue SyntaxError => e
self.settings = settings_edit
end
end
end
Then in your form use <%= f.text_area :settings_edit %>.
I have not tested any of this code, but in theory it should work. Good luck!
WARNING: Using eval like this is very dangerous, in this example a user could delete the entire Template table with one line in the edit box Template.destroy_all. Use a different method to convert the string to a hash if user input is involved.
... or you could use something like this (without any logic in model):
<% #template.settings.each do |name, value| %>
<div>
<%= label_tag name %>
<%= text_field_tag "template[settings][#{name}]", value %>
</div>
<% end %>
you should use something like
class Template < ActiveRecord::Base
serialize :settings, Hash
end

Resources