Ruby on Rails Form for Calculating Dice Rolls - ruby-on-rails

I am creating a web application as a training project that will take three inputs (Integer values for number of dice, number of sides and number of rolls) and input that into a series of tables that will facilitate calculations and the output of the results, and the saving of historical data (past rolls.)
I'm stuck.
Here is my ERB for the form:
<%= form_for(#run) do |f| %>
How many dice: <%= f.number_field :die_count, :placeholder => "# of dice" %><br>
How many sides: <%= f.number_field :die_sides, :placeholder => "# of sides" %><br>
How many rolls: <%= f.number_field :rolls, :placeholder => "# of rolls" %><br>
<%= f.submit "Roll!" %>
<% end %>
Here's my Model:
class Run < ActiveRecord::Base
has_many :rolls
belongs_to :user
validates_presence_of :die_count, :rolls, :die_sides
validates_numericality_of :die_count, :die_sides, :rolls
end
Here's my pertinent Controller info:
def new
#run = Run.new
end
def create
#run = Run.new(run_params)
end
private
def run_params
params.require(:run).permit(:die_count, :die_sides, :rolls)
end
For good measure, here's the migrate for making my DB table:
class CreateRuns < ActiveRecord::Migration
def change
create_table :runs do |t|
t.integer :die_count
t.integer :die_sides
t.integer :roll_count
t.timestamps null: false
end
end
end
At this point, I just want it to create a new record in the Run model. Here's the error I'm getting with the input of 5, 5, and 5 into the number_fields and click my submit button:
If I am reading this correctly, my application wants to iterate through the individual numbers as if they were arrays, but it can't because they are strings. This confuses me because they aren't strings in the first place. They are integers.
Pulling my hair out on this one.

:rolls is a reference to another model called Roll (have you created it?).
:roll_count is the attribute of your Run model that defines the number of rolls (as visible in your migration table).
They are two very different things, even though it stands to reason that :roll_count should in theory be equal to the number of Roll records your Run record 'owns'.
In your form, replace :rolls by :roll_count. Do the same in the run_params method in your controller. And also in the two validations in your model. (checklist: that's 4 places where you need to modify that symbol name).
In your controller, also change the content of your create method to #run = Run.create(run_params).
Edited (clarity): Run.new(params) creates a new instance of a Run object but doesn't save it in your DB. Run.create(params) creates the object AND saves it.
Finally, in your model, comment out the has_many :rolls until you have actually implemented the Roll model (no pun intended!) ; do the same for belongs_to :user if the User model is not implemented either.
After these changes, your form should normally work and data should be saved in your runs database table.

I believe you meant to replace rolls with roll_count everywhere in your code.
How many rolls: <%= f.number_field :roll_count, :placeholder => "# of rolls" %><br>
and
validates_presence_of :die_count, :roll_count, :die_sides
validates_numericality_of :die_count, :die_sides, :roll_count
and
def run_params
params.require(:run).permit(:die_count, :die_sides, :roll_count)
end

Related

How do I call the name of a user when they belong to a model

I have two models, Chasing and User, a chasing belongs_to :user and a user has_many :chasings.
I created a migration for linking the two models together:
class AddUsersToChasings < ActiveRecord::Migration
def change
add_reference :chasings, :user, index: true, foreign_key: true
end
end
I have a controller for creating new users which I then want to be able to assign to chasings. I currently have this code in my chasings form for selecting the user:
<%= f.select :user_id, options_for_select(User.all.map {|c| [c.name, c.id]}), { :include_blank => "Please select user"}, {:class => "form-control"} %>
This seems to do the trick, after calling Chasing.first in rails console I can see the chasing now has user_id relevant to the user I picked. I can also run Chasing.first.user.name to give me the name of the user who is associated with the chasing. I'm wanting to show this name in my index view, the code I currently have for this is:
ChasingsController:
def index
#chasing = Chasing.all
end
Index view:
<% #chasing.each do |chasing| %>
<%= chasing.user %>
<% end %>
This shows a random string (seems to change every time I update a chasing - #<User:0xf5b0ba8> for example). when I change this to chasing.user.name I get 'undefined method `name' for nil:NilClass'.
Is there a way I can call the name for my view?
EDIT:
As per NickM's comment below I had chasings without users assigned to them causing active record to throw the error.
Looks like you have some Chasing objects in your database without user_ids. You can test by doing <%= chasing.user.name if chasing.user %>

How to default collection_check_boxes to checked?

I have this line that I'm trying to default to checked <%= f.collection_check_boxes :committed, checked, Date::ABBR_DAYNAMES, :downcase, :to_s, %>
In db t.text "committed".
I tried variations of checked & true, but maybe I overlooked something.
Here's the Gist of it.
Here is a quick answer on how to add checked as the default to the collection_check_boxes form helper since it took me some time to figure it out. Break it into a block and you can set checked and add classes. More info at http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_check_boxes.
<%= f.collection_check_boxes(:author_ids, Author.all, :id, :name) do |b| %>
<%= b.label(class: "check_box") { b.check_box(checked: true, class: "add_margin") + b.text } %>
<% end %>
You are using a form_for, so that f is a form builder. This means that it is bound to the object you initialized it with, let's call it #habit. Since you're calling collection_check_boxes on the form builder, it will do something like #habit.send(:commit) to consult whether or not it should have the check box checked, and currently (apparently) it is not. In other words, if you want to use form_for you need to have this "everything is checked" fact represented in the model itself.
Now I am not sure what your model layer looks like, so I'll address a few scenarios. If you have a has_and_belongs_to_many relationship like this:
class Habit < ActiveRecord::Base
has_and_belongs_to_many :committed_days
end
class CommittedDay < ActiveRecord::Base
has_and_belongs_to_many :habits
# let's assume it has the columns :id and :name
# also, let's assume the n:m table committed_days_habits exists
end
Then I think the easiest way is in the controller itself do something like this:
def new
#habit = Habit.new
#habit.committed_day_ids = CommittedDay.all.map(&:id)
end
And in your ERB do:
<%= f.collection_check_boxes(:committed_day_ids, CommittedDay.all, :id, :name)
Now, it might be an overkill to do this with a has-and-belongs-to-many, especially with days of week (it means the CommittedDay table has 7 records, one for each day, which is kinda awkward). So you could also consider simply serializing an array of days of week into the db, and then just make sure the default for that column contains all of them.
The ERB will be similar to what you wrote:
<%= f.collection_check_boxes :committed, Date::ABBR_DAYNAMES, :downcase, :to_s %>
If you're using Postgres your class can be simply:
class Habit < ActiveRecord::Base
end
And the serialization code would be in the migration:
# downcase is used since in the ERB you are using :downcase for the id method
t.text :committed, default: Date::ABBR_DAYNAMES.map(&:downcase), array: true
If you are not using Postgres, you can use Rails serialization which is DB agnostic:
class Habit < ActiveRecord::Base
serialize :committed, Array
end
And then your migration would look like this:
t.text :committed, default: Date::ABBR_DAYNAMES.map(&:downcase).to_yaml

select_check_boxes with an has many through relation and :checked option

I am using collection_check_boxes to create object in a has_many through relation;
here some models:
#emotion.rb
class Emotion < ActiveRecord::Base
has_many :emotional_states
has_many :reports, :through => :emotional_states
end
#report.rb
class Report < ActiveRecord::Base
has_many :emotional_states
has_many :emotions, :through => :emotional_states
[... other "irrelevant" stuff here ...]
end
#emotional_states.rb
class EmotionalState < ActiveRecord::Base
belongs_to :report
belongs_to :emotion
end
As you may understand when I create a Report I also select with a collection_check_box a list of Emotions I want to bind to that report (through the model EmotionalState); Everything works on create (I retrieve the hash values and if #report.save I also create EmotionalStates with the #report.id and #emotion.id.)
But when it cames to edit the Report I would like to edit also the associated EmotionalStates (this means creating new EmotionalStates or deleting old one).
How can I populate the select_check_boxes with ALL the available Emotions having checked that emotions that are alredy associated through the EmotionalStates bojects?
If I write something like:
<%= collection_check_boxes(:report, :emotion_id, #report.emotional_states.map{|e| e.emotion}, :id, :name) %>
I'll get a unchecked checkbox for every alredy associated Emotion.
<%= collection_check_boxes(:report, :emotion_id, Emotion.all, :id, :name, :checked => #report.emotional_states.map{|e| e.emotion}) %>
While this code will correctly returns Emotion.all, but will not check the emotions alredy associated to #report through #report.emotional_states.
I've searched all around the wheb for examples on the usage of :checked options for collection_select_boxes without any results...
any hint?
I did the same once in this way.you can also try :
Emotions:
<% Emotion.all.each do |emotion| %>
<%= check_box_tag 'report[emotion_ids][]' , emotion.id, #report.emotion.include?(emotion) %><%= emotion.name %><br/>
<% end %>
In Controller add :emotion_ids=>[] into strong parameters.
And one line into controller update method:
params[:report][:emotion_ids] ||= []
After coming back to this bug I discovered that the problem was an incorrect use of the .map method, mapping a whole object (e.emotion) instead its id (e.emotion.id).
This easily fixed my problem:
<%= collection_check_boxes(:report, :emotion_id, Emotion.all, :id, :name, :checked => #report.emotional_states.map{|e| e.emotion.id}) %>
Thank you for your help!

Rails Drop Down Menu based on new Model

I've been trying to work through this for a few days and can't get anything to work. I have been building my first app based on Michael Hartl's amazing tutorial: http://ruby.railstutorial.org/. Additionally, I have tried this tutorial, but the differences in my code and his prove to be too great for me to follow along.
Where my app differs from Michael Hurtl's is that I am trying to create a site where you can post your left over cans of paint (instead of microposts, AKA twitter). When I created the app, I had a column in the Paints model called "color_family". Now I am looking to change it from a text field to a drop down with predetermined values, e.g. "Reds", Oranges", "Yellows", Greens" etc.
I started out by generating a new scaffold:
rails generate scaffold Color_Family family:string
then I generated a migration:
rails generate migration AddColor_FamilyToPaints family_id:int
and migrated it all.
Then I created the associations
class ColorFamily < ActiveRecord::Base
has_many :paints
end
and
class Paint < ActiveRecord::Base
attr_accessible :family_id, :name, :hex, :location, :quantity, :additional_info
belongs_to :user
belongs_to :color_family
...
end
This is where I get lost, and any tutorial I try to follow breaks everything. Where do I define my predetermined list of color_families?
Is it even worth it for me to go through the creation of a new model? I previously tried this in the form field:
<%= form_for(#paint) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.label :color_family %>
<%= select_tag(:color_family, options_for_select([['Red', 1],
['Orange', 2],
['Yellow', 3],
['Green', 4],
['Blue', 5],
['Purple', 6],
['Black', 7],
['Grey', 8],
['White', 9],
['Cream', 10],
['Brown', 12]])) %>
and while it created a dropdown for me, it never captured the info when I added a new paint.
Any help is greatly appreciated. Also, a link to a tutorial would probably do me the biggest help as I've very new to RoR and backend stuff in general.
I'm not sure if you are doing the reading version of the book, or the video. Personally, I recommend both! Absolutely amazing tutorial! One of the first things he does mention though, "Scaffold is not really for the real world" and you should consider this. When I'm doing projects, new old or just refactoring, I usually add everything by hand with the script/generate. The only "scaffold" I've ever used was the scaffold_controller because I was too lazy to do the controller by hand.
The short answer, you should have another model "Color" and the form should:
f.collection_select(:color_id, Color.find(:all), :id, :name, {:include_blank => 'Please Select A Color'})
And the ColorFamily should probably be a has_many_and_belongs_to_many Colors
If you could give me a run down of details associations supposed to be taking place, I can write up a small data modal for you.
Edit #1
You are needing a has_one :through relationship. The general concept will be...
Pivot tabel:
rails g migration ColorFamilyPaints paint_id:integer color_family_id:integer
Paint Class:
class Paint < ActiveRecord::Base
attr_accessible :family_id, :name, :hex, :location, :quantity, :additional_info,
:color_families_attributes # Need to add this in order for you to be able to post with drop down
belongs_to :user
...
# Creates the Relationship
has_one :color_families, :through => :color_family_paints
# Allows color families to be nested in the form.
accepts_nested_attributes_for :color_families, :allow_destroy => true
end
You'll notice a few changes. Addition to the attr_accessible, and the accepts_nested_attributes_for (you may need this, not sure with a has_one though). When you build the form, look at the ID/Name of the select box. If it ends in _attributes, use the accepts line. If not, you don't need it. Alter the :color_families_attributes to match the name of the select box.
Form HTML:
<%= form_for(#paint) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.label :color_family %>
<%= f.collection_select(:color_family, ColorFamily.find(:all), :id, :name, {:include_blank => 'Please Select Color Family'}) %>
</div>
<% end %>
More information on associations # RoR website.

Validating field's presence fails even though the field is not blank

I'm trying to fill out an array with values from checkboxes. It works just fine when creating a record, but fails validation when editing. The params look right, which is what really confuses me:
"record"=>{... "type_array"=>["accounting"], ...}
It looks the same as the params from creating a new record. The fields in New.html.erb and Edit.html.erb also use the same markup.
Edit.html.erb
<div class="field">
<%= f.label :type_array, "What type of record?" %><br />
<% ["accounting", "agriculture", "automotive"].each do |type| %>
<%= check_box_tag 'record[type_array][]', type, (true if #record.type_list.include? type),
:id => type %>
<%= label_tag type, type.titleize, :class => type %><br />
<% end %>
</div>
Parts of Record.rb
validates :type_array, :presence => true
attr_accessor :type_array
attr_accessible :type_array
before_validation :set_type_list
private
def set_type_list
self.type_list = type_array.join ',' if type_array.present?
end
Am I missing something? When I remove the type_array validation and fill out the form, it acts like type_array is empty. Somewhere along the line, it must be lost or something.
I appreciate any help.
(Sidenote: if anyone has a better way to do the list of checkboxes, let me know)
Delete the line attr_accessor :type_array.
This creates accessor methods to a new instance variable, not to the model attribute type_array, which means that #record.type_array now refers to that instance variable instead of the attribute.
You almost never use attr_accessor or it's siblings attr_reader and attr_writer in Rails because you want to deal with model attributes, not instance variables.
Edit: You're using type_array as a virtual attribute.
class Record < ActiveRecord::Base
validates :type_array, :presence => true
attr_accessible :type_array
def type_array=(val)
self.type_list = val.join ','
end
def type_array
self.type_list.split ','
end
def type_array_before_type_cast
type_array
end
end
For the reason why you need that last function definition, see this question.

Resources