I am just at the very beginning of learning rails and ruby but am already stuck.
I have created a model, a view file and a controller with a form that allows the user to enter some data. Now I am working on another MVC that makes various calculations with these data and returns the result. My problem is: I simply don't understand how I get the data entered by the user to the model that makes the calculations. I know this is a very beginner's question, but well, looks like I can't solve it alone.
Entries Model: entry.rb
class Entry
include ActiveModel::Model
attr_accessor :icao, :string
attr_accessor :elevation, :integer
attr_accessor :qnh, :integer
attr_accessor :temperatur, :integer
attr_accessor :winddirection, :integer
attr_accessor :windspeed, :integer
attr_accessor :basedistance, :integer
validates_presence_of :icao
validates_presence_of :elevation
validates_presence_of :qnh
validates_presence_of :temperatur
validates_presence_of :winddirection
validates_presence_of :windspeed
validates_presence_of :basedistance
end
Entries View: new.html.erb
<% content_for :title do %>Flytical<% end %>
<h3>Airfield Conditions</h3>
<div class="form">
<%= simple_form_for #entry do |form| %>
<%= form.error_notification %>
<%= form.input :icao, label: 'Airfield ICAO', input_html: { maxlength: 4 }, autofocus: true%>
<%= form.input :elevation, label: 'Airfield Elevation', input_html: { maxlength: 4 }%>
<%= form.input :qnh, label: 'QNH', input_html: { maxlength: 4 }%>
<%= form.input :temperatur, label: 'Temperatur', input_html: { maxlength: 3 }%>
<%= form.input :winddirection, label: 'Wind Direction', input_html: { maxlength: 3 }%>
<%= form.input :windspeed, label: 'Wind Speed', input_html: { maxlength: 3 }%>
<%= form.input :basedistance, label: 'Base Takeoff Distance at MSL', input_html: { maxlength: 4 }%>
<%= form.button :submit, 'Submit', class: 'submit' %>
<% end %>
</div>
Entries Controller: entries_controller.rb
class EntriesController < ApplicationController
def new
#entry = Entry.new
end
def create
#entry = Entry.new(secure_params)
if #entry.valid?
flash[:notice] = "Entries for #{#entry.icao} received."
render :new
else
render :new
end
end
private
def secure_params
params.require(:entry).permit(:icao, :elevation, :qnh, :temperatur, :winddirection, :windspeed, :basedistance)
end
end
Now for the calculations. This is the Calculation model: calc.rb
You see I tried to get the data with #entries.xxx, but obviously that did not work
class Calc
include ActiveModel::Model
def pressalt
if #entry.qnh < 1013
pressalt = #entry.elevation + (1013 - #entry.qnh) * 30
else
pressalt = #entry.elevation
end
end
def densalt
if #entry.temperatur > 15
densalt = pressalt + (((120 * ((#entry.temperatur - (15 - #entry.elevation * 2 /1000)))
else
densalt = pressalt
end
end
end
Calculation view: new.html.erb
<% content_for :title do %>Flytical<% end %>
<h3>Results</h3>
<p> Test result <%= #densalt %> </p>
Calc controller: calcs_controller.rb
class CalcsController < ApplicationController
def create
#calc = Calc.new
end
end
And these are my routes:
Rails.application.routes.draw do
resources :entries, only: [:new, :create]
resources :calcs, only: [:new, :create]
root to: 'visitors#new'
end
Honestly, I have the impression that I am missing some basic, but major points - my hope is that I am going to learn it with this project.
Related
I've got an app where the user has to fill out a survey. I need to store user's answers in inside TestResult model which have only one field answers:string
With current implementation I'm getting params from the form as:
params => {
{
"question_#{id}": "some answer 1",
"question_#{id}": "some answer 12345",
}
}
Which I want to change to the below structure:
# expected hash params
params => {
{
question: 'first question',
answer: 'some answer 1'
},
{
question: 'second question',
answer: 'some answer 123431'
}
}
What should I change (probably in a view) to get this hash?
new.html.erb
<%= simple_form_for :test_results, url: test_results_path do |f| %>
<% #randomize_questions.map do |q| %>
<%= q[:question] %>
<%= f.input "question_#{q[:id]}", collection: q[:answers], as: :radio_buttons %>
<% end %>
<%= f.button :submit %>
<% end %>
controller:
class TestResultsController < ApplicationController
before_action :fetch_random_questions, only: [:new, :create]
def new
#test_result = TestResult.new
end
def create
#test_result = TestResult.new(
answer: test_result_params,
)
#test_result.save
redirect_to dummy_path
end
end
private
def test_result_params
params.require(:test_results).permit!
end
def fetch_random_questions
TestQuestion.where(published: true).order('RANDOM()')
#randomize_questions = test_questions.map do |obj|
{
id: obj.id,
question: obj.question,
answers: [obj.correct_answer, obj.other_answer1, obj.other_answer2, obj.other_answer3],
}
end
end
end
TestResult model
class TestResult < ApplicationRecord
serialize :answer, Hash
serialize :answer, String
validates :answer, presence: true
end
The params get his structure from the input names.
So you could add an hidden field for question, and then specify a name for both of your fields.
<%= simple_form_for :test_results, url: test_results_path do |f| %>
<% #randomize_questions.map do |q| %>
<%= q[:question] %>
<%= f.input "question_#{q[:id]}", as: :hidden, input_html: { name: "test_results[#{q[:id]}][question]", value: q[:question] } %>
<%= f.input "question_#{q[:id]}", collection: q[:answers], as: :radio_buttons, input_html: { name: "test_results[#{q[:id]}][answer]" } %>
<% end %>
<%= f.button :submit %>
<% end %>
Params should looks like this:
params => {
test_result: {
1 => {
question: "...",
answer: "..."
},
2 => {
question: "...",
answer: "..."
}
}
}
Not tested. Could you tell if that's works for you?
I am trying to create a form that updates 2 tables - commission_type and commission_tier.
I created the models, controller and form but when I submit it, my commission_tier table does not update. Only my commission_type table updates.
Can someone take a look at my code and tell me what I am doing wrong? I have combed through my code trying to find the mistake, and I cannot find it.
My models
class CommissionType < ApplicationRecord
has_many :commission_tiers
accepts_nested_attributes_for :commission_tiers
end
class CommissionTier < ApplicationRecord
belongs_to :commission_types, optional: true
end
My controller
class Admin::CommissionTypesController < Admin::BaseController
def index
#commission_types = CommissionType.all
end
def new
#commission_type = CommissionType.new
#commission_type.commission_tiers.build
end
def create
#commission_type = CommissionType.new(commission_type_params)
if #commission_type.save
redirect_to admin_commission_types_index_path
else
render "new"
end
private
def commission_type_params
params.require(:commission_type).permit(:name, :active, :allow_mass_update, :plan,
commission_tiers_attributes: [:id, :increment_value, :rate, :commission_type_id])
end
end
My form
<%= simple_form_for #commission_type, url: admin_commission_types_index_path, wrapper: :bootstrap2, :html => { :class => 'form-horizontal' } do |f| %>
<fieldset>
<legend>Properties</legend>
<%= f.input :name, label: 'Commission Name' %>
<%= f.input :active, as: :boolean, label: 'Active?', label_html: { class: 'padding-top' } %>
<%= f.input :allow_mass_update, as: :boolean, label: 'Allow mass update?', label_html: { class: 'padding-top' } %>
<%= f.input :plan, input_html: {id: 'dropdown'},
label: 'Commission Type',
collection: [ ['Select One..', 'select'], ['Flat', 'flat'], ['Flat +', 'flat_plus' ], ['Promotional', 'promotional'], ['Straight', 'straight'], ['Waterfall', 'waterfall'], ['Sliding Scale', 'sliding_scale'] ],
selected: 'select'
%>
</fieldset>
<fieldset id="flat">
<legend>Flat Commission</legend>
<%= f.simple_fields_for :commission_tiers do |builder| %>
<%= builder.input :rate %>
<%= builder.input :increment_value %>
<% end %>
</fieldset>
My form is displaying and working
UPDATE
Some additional details
CommissionType column values = [:name, :active, :allow_mass_update, :plan]
CommissionTier column values = [:id, :increment_value, :rate, :commission_type_id]
Also, when I submit my form, here is an example of what my params are
<ActionController::Parameters {"name"=>"asdf", "active"=>"1", "allow_mass_update"=>"1", "plan"=>"flat", "commission_tiers_attributes"=><ActionController::Parameters {"0"=><ActionController::Parameters {"rate"=>"45"} permitted: true>} permitted: true>} permitted: true>
I'm using ActiveAdmin for an admin panel. In the nested form, I need to use some filtering, for example, in the dropdown I need to select an option name, and in the second dropdown should be displayed the option's values.
For this, I want to use activeadmin-ajax_filter gem. Option and OptionValue are connected with each other: Option has_many :option_values, and OptionValue belongs_to Option. I create option_values through the nested form when I create the Option.
Then, I have a Product model, in which via nested form I create a Variant. So, Variant belongs_to :option_value belongs_to :product and OptionValue has_many :variants, Product has_many :variants.
Just for now, when I want to create a new variant, I can select all the option values I have in the db. I want to select an option (for example a size), and then, in a dropdown below - select the appropriate values (XL, S, and so on).
The code for my active_admin resources is:
ActiveAdmin.register Product do
permit_params :category_id, :name, :description, :short_description, :image, :subtitle,
product_currencies_attributes: [:id, :product_id, :currency_id, :price, :_destroy],
variants_attributes: [:id, :product_id, :in_stock_id, :sold_out_id, :option_value_id, :image, :visible,
:orderable, :_destroy]
index do
column :id
column :image do |product|
image_tag(product.image.url(:thumb)) if product.image
end
column :category_id do |product|
category = Category.find(product.category_id)
link_to category.name, admin_category_path(category)
end
column :name
column :short_description
column :description do |product|
product.description.truncate(90)
end
column :subtitle
column :created_at
column :updated_at
actions
end
show do
tabs do
tab 'Product' do
attributes_table do
row :id
row :image do
image_tag(product.image.url(:medium))
end
row :category_id do
category = Category.find(product.category_id)
link_to category.name, admin_category_path(category)
end
row :name
row :short_description
row :description
row :subtitle
row :created_at
row :updated_at
end
end
tab 'Prices' do
attributes_table do
row :prices do
div do
product.product_currencies.each do |product_currency|
div do
"#{Currency.find(product_currency.currency_id).name}: #{product_currency.price}"
end
end
end
end
end
end
tab 'Variants' do
div do
'Variants will be here soon =)'
end
end
end
end
form do |f|
tabs do
tab 'Product' do
f.inputs do
f.input :category_id, as: :select, collection: Category.all.collect { |category| [category.name, category.id] }
f.input :name
f.input :short_description
f.input :description
f.input :subtitle
f.input :image, as: :file
end
end
tab 'Price' do
f.inputs do
f.has_many :product_currencies, allow_destroy: true, heading: false, new_record: 'Add New Price' do |product_currency|
product_currency.template.render partial: 'product-price-form', locals: { product_currency: product_currency,
product_id: params[:id].to_i }
end
end
end
tab 'Variants' do
f.inputs do
f.has_many :variants, allow_destroy: true, heading: false, new_record: 'Add new Variant' do |variant|
variant.template.render partial: 'variant-form', locals: { variant: variant, product_id: params[:id].to_i }
end
end
end
tab 'SEO' do
div do
'SEO inputs will be here'
end
end
end
f.actions
end
end
So, I've installed activeadmin-ajax_filter gem. I tried to follow the documentation, but as a result - nothing. As far, as I understand, I have to put this line of code to my Option activeadmin model: activeadmin-ajax_filter, and in the form use this:
f.input :option, as: :ajax_select, data: {
url: filter_admin_options_path,
search_fields: [:name],
static_ransack: { active_eq: true },
ajax_search_fields: [:option_value_id],
}
But still - nothing. By the way, the partial is:
<% if #product.new_record? %>
<% if Product.any? %>
<% new_product_id = Product.order(id: :desc).first.id + 1 %>
<% variant.input :product_id, as: :hidden, input_html: { value: new_product_id } %>
<% else %>
<% variant.input :product_id, as: :hidden, input_html: { value: 1 } %>
<% end %>
<% else %>
<% variant.input :product_id, as: :hidden, input_html: { value: product_id } %>
<% end %>
<% variant.input :in_stock_id, as: :select, collection: InStock.all.collect { |in_stock| [in_stock.name, in_stock.id] } %>
<% variant.input :sold_out_id, as: :select, collection: SoldOut.all.collect { |sold_out| [sold_out.name, sold_out.id] } %>
<%# variant.input :option, as: :ajax_select, collection: Option.all.collect { |option| [option.name, option.id] },
# data: {
# url: filter_admin_options_path,
# serarch_fields: [:name],
# static_ransack: { active_eq: true },
# ajax_search_fields: [:option_value_id]
# }
#%>
<% variant.input :option_value_id, as: :select, collection: OptionValue.all
.collect { |option_value| [option_value.value, option_value.id] } %>
<% variant.input :visible %>
<% variant.input :orderable %>
<% variant.input :image, as: :file %>
In my Rails application I have simple search functionality. I want to extract to Form Object but don't know how to do. I have search form which looks like this:
.row
= horizontal_simple_form_for :cars, {url: cars_path, method: :get} do |f|
.col-md-4
.row
.col-md-12
= f.input :handover_location, label: I18n.t('.handover'), collection: Location.all.map{|hl| [hl.location_address, hl.id]}
= f.input :return_location, label: I18n.t('.return') ,collection: Location.all.map{|rl| [rl.location_address, rl.id]}
= f.input :car_class, label: I18n.t('.car_class') ,collection: CarClass.all.map { |c| [c.name, c.id] }, include_blank: true
.col-md-4
= f.input :handover_date, as: :string, label: false
= f.input :return_date, as: :string, label: false
= f.submit class: 'btn btn-success'
Cars controller:
class CarsController < ApplicationController
skip_authorization_check
def index
#cars = Car.search(params)
end
def show
end
end
And class method in Car model which search correct cars:
def self.search(params)
self.joins(:reservations).where.not("reservations.reception_time <= ? AND reservations.return_time >= ?",
params[:cars][:return_date], params[:cars][:handover_date]).
joins(:car_class).where("car_classes.id= ?", params[:cars][:car_class])
.cars_at_both_locations(params[:cars][:handover_location], params[:cars][:return_location])
end
Now I'm trying to extract this to Form Object. I've created a file search_form.rb:
class SearchForm
include ActiveModel::Model
attr_accessor :handover_date, :return_date, :handover_location, :return_location, :car_class
end
But now I don't know how to handle my params to this form object. Thank's in advance.
I wish I could help you with the Form Object stuff, but I need to learn more about classes & modules
I can help you with the search functionality, as we've done it before here
Here's the code we used:
#View
<%= form_tag search_path, :method => :post, :id => "SearchForm" do %>
<%= text_field_tag :search, params[:search], placeholder: 'Search your favourite products or brands', :autocomplete => :off %>
<%= image_submit_tag 'nav_bar/search.png' %>
<% end %>
#config/routes.rb
match 'search(/:search)', :to => 'products#search', :as => :search, via: [:get, :post]
#app/controllers/products_controller.rb
def search
#products = Product.search(params[:search])
respond_to do |format|
format.js { render :partial => "elements/livesearch", :locals => {:search => #products, :query => params[:search]} }
format.html { render :index }
end
end
Notice the form_tag we used?
Simple form does not work with form_tag currently (it requires an object) - we just send the data with a GET request to the controller & that then sends the data to the Product model
I think your problem will be caused by the use of your SearchForm object. You only need this because your use of simple form means you have to pass an object. Problem being this is not necessary for search
A better way will be to use a standard form_tag, and send the request directly to your controller. This will allow you to process the data as params, which you'll be able to send directly to your Car model
--
I can write some code specific to you if you want
I found solution on my own.
Cars controller:
def index
#search_form = SearchForm.new(params[:search_form])
#cars = #search_form.submit(params[:search_form])
end
search_form.rb:
class SearchForm
include ActiveModel::Model
attr_accessor :handover_date, :return_date, :handover_location, :return_location, :car_class
def submit(params)
Car.search(params)
end
end
Search form in view:
.row
= horizontal_simple_form_for SearchForm.new, {url: cars_path, method: :get} do |f|
.col-md-4
.row
.col-md-12
= f.input :handover_location, label: I18n.t('.handover'), collection: Location.all.map{|hl| [hl.name, hl.id]}
= f.input :return_location, label: I18n.t('.return') ,collection: Location.all.map{|rl| [rl.name, rl.id]}
= f.input :car_class, label: I18n.t('.car_class') ,collection: CarClass.all.map { |c| [c.name, c.id] }, include_blank: true
.col-md-4
= f.input :handover_date, as: :string, label: false
= f.input :return_date, as: :string, label: false
= f.submit class: 'btn btn-success'
search method in car model:
def self.search(params)
self.joins(:reservations).where.not("reservations.reception_time <= ? AND reservations.return_time >= ?",
params[:return_date], params[:handover_date]).
joins(:car_class).where("car_classes.id= ?", params[:car_class])
.cars_at_both_locations(params[:handover_location], params[:return_location])
end
I have two models #product and #photo with #photo nested in #product. I am only allowed to use one form to create both. I am using this JQuery plugin to handle photo upload being how it gives me a nice preview.
However the plugin has certain restrictions in my create action so I cannot use the product create action to handle both creating the photo and product.
Is it possible to have one nested for_for use two different controllers?
And how would i do it ?
my form (haml)
= form_for #product,:url => products_path, :html => { id: "fileupload", multipart: true } do |f|
%p
= f.text_field :name, placeholder: "Name"
%p
= f.text_field :price, class: "auto", data: { a_sign: "$ " }, placeholder: "Price"
%p
= f.text_field :description, placeholder: "Description"
%p
= f.fields_for :photos do |fp|
=fp.file_field :image
%br
.files{"data-target" => "#modal-gallery", "data-toggle" => "modal-gallery"}
%p.button.start
= f.submit
You can use accept_nested_attributes for to save associated data with only one create action.
Eg:-
class AlbumsController < ApplicationController
def new
#album = Album.new
#album.photos.build
end
def create
#album = Albums.new(params[:album])
#album.photos.build unless #album.photos.present?
if #album.save
flash[:notice] = "Successfully created albumn"
respond_with(#album, :location => albums_path())
else
flash[:error] = #album.errors.full_messages
render :new
end
end
end
Model:-
class Album < ActiveRecord::Base
has_many :photos, dependent: :destroy
accepts_nested_attributes_for :photos, allow_destroy: true, reject_if: proc {|attr| attr['image'].blank? }
end
class Photo < ActiveRecord::Base
belongs_to :album
end
View:-
= form_for #album,:url => albums_path, :html => {multipart: true } do |f|
%p
= f.text_field :name, placeholder: "Name"
%p
= f.text_field :price, class: "auto", data: { a_sign: "$ " }, placeholder: "Price"
%p
= f.text_field :description, placeholder: "Description"
%p
= f.fields_for :photos do |photo|
= photo.file_field :image
%br
.files
%p.button.start
= f.submit
Use ajax to firstly upload photo and after successful response continue with product.