How to make ruby on rails text_field to accept regex? - ruby-on-rails

I would like user to input regex using form and text_field tag. But as I understand it is being sanitized by default. If I store regexs directly in DB via seed.rb or console, it works. But I cannot do it using UI:
<div class="control-group">
<%= f.label :regexp, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :regexp, :class => 'text_field' %>
</div>
</div>
How to fix it?

Here is a solution:
Overriding the getter method with a regex parsing method that you define on the model will work. So now if you enter in a regex through the UI like /\d+/ it will serialize to "/\\d+/" to store in the database. But when you call the instance of user.regex it will be parsed into a Regexp object which you can call match on.
Example:
u = User.create(regex: "/\\d+/")
u.regex => /\d+/
u.regex.class => Regexp
u.regex.match("12345") => #<MatchData "12345">
u.regex.match("12345abc") => #<MatchData "12345">
Model:
class User < ActiveRecord::Base
def regex
parse_regex(self[:regex])
end
def parse_regex(string)
pieces = string.split("/")
pattern = pieces[1]
flags = pieces[2]
arg_two = 0
if flags.present?
flags.length.times do |i|
case flags[i, 1]
when "i"
arg_two |= Regexp::IGNORECASE
when "m"
arg_two |= Regexp::MULTILINE
when "x"
arg_two |= Regexp::EXTENDED
end
end
end
Regexp.new(pattern, arg_two)
end
end
Controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params).save
end
private
def user_params
params.require(:user).permit(:regex)
end
end
View:
<%= form_for #user do |f| %>
<%= f.label :regex %>
<%= f.text_field :regex %>
<%= f.submit %>
<% end %>

Related

How do I save two objects at once in a Rails form?

I’m using Rails 4.2.3. How do I save two objects with one form? I have these two models …
class MyObject < ActiveRecord::Base
belongs_to :user
has_many :my_object_times
attr_accessor :hour, :minute, :second
accepts_nested_attributes_for :my_object_times
end
and
class MyObjectTime < ActiveRecord::Base
belongs_to :my_object
end
I want to submit a single form that creates both a MyObject and a MyObjectTime object. So I created my form
<%= form_for #my_object do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label "Time" %>
<%= select_tag('my_object[hour]', options_for_select((0..12).to_a), {:prompt => 'Select Hour'} ) %> hrs
<%= select_tag('my_object[minute]', options_for_select((0..60).to_a), {:prompt => 'Select Minutes'} ) %> min
<%= select_tag('my_object[second]', options_for_select((0..60).to_a), {:prompt => 'Select Seconds’} ) %> sec
<%= f.fields_for :my_object_time do |mot| %>
<%= mot.number_field :time_in_ms %>
<% end %>
</div>
<div class="actions">
<%= button_to "Save", { :action => "create" }, :method => :post, :class => 'button' %>
</div>
<% end %>
and here’s my controller …
def create
#my_object = MyObject.new(my_object_params)
#current_user = User.find(session["user_id"])
#my_object.user = #current_user
if #my_object.save
respond_to do |format|
format.html { redirect_to controller: "users", action: "index", notice: 'Saved successfully.' }
end
else
format.html { render action: "index" }
end
end
private
def my_object_params
# Main Code goes here
params[:my_object][:time_in_ms] = (params[:my_object][:hour].to_i * 60 * 60 + params[:my_object][:minute].to_i * 60 + params[:my_object][:second].to_i) * 1000
params.require(:my_object).permit(:name, my_object_times_attributes: [:time_in_ms])
end
However, only the MyObject object is getting saved and not a corresponding MyObjectTime. Is there a way I can get both to get created at once? I don’t want a situation where one gets created and the second does not. I want either both to get created or nothing to happen at all and an error returned.
Since you have has_many :my_object_times, you should use my_object_times(plural) instead of my_object_time(singular) in fields_for and in my_object_params method
<%= f.fields_for :my_object_times do |mot| %>
And make sure you have build the child with the parent in the new method
#my_object_controller
def new
#my_object = MyObject.new
#my_object.my_object_times.build
end

Nested Attributes Child model don't save

I'm having a problem in the model saving with nested attributes.
In the app, there's a Customer, that have 1..n Contacts witch in turn have 1..n Telephones.
I've searched a lot before asking here, and decided to make it save only the Contact first. Well, at first the Customer is stored, but Contact is not. From what I read there's no need to repeat the ... contacts.build from new function in the create, and that the line "#customer = Customer.new(customer_params)" would create and store them both.
Why it's not working? (That's the first question.)
After some modifications and debugging, I found that when I set a second line building Contact (...contacts.build(customer_params[:contacts_attributes])) it's not saved because of an error of 'unknown attribute'. That's because between the hash :contacts_attribute and the content of it, it's added another hash, called ':0' (?). The structure of the hash that comes from the form is this :
":contacts_attribute[:0[:name, :department, :email]]"
I imagine that this hash :0 is for adding more than one Contact instance, that will come in hashes :1, :2 etc.
There's a way to store the Contact instance by getting this :0 hash? (How do I access this hash? Is it "... :contacts_attribute[0]"?)
Below is the relevant code.
Thanks for the attention!
customer.rb
class Customer < ActiveRecord::Base
...
has_many :contacts
accepts_nested_attributes_for :contacts, reject_if: lambda {|attributes| attributes['kind'].blank?}
...
def change_by(user_id)
update_attributes(changed_by: user_id, deleted_at: Time.now, updated_at: Time.now)
end
def delete(user_id)
update_attributes(status: false, changed_by: user_id, deleted_at: Time.now, updated_at: Time.now)
end
private
...
end
customers_controller.rb
class CustomersController < ApplicationController
def new
#customer = Customer.new
#customer.contacts.new
end
def create
user_id = session[:user_id]
#customer = Customer.new(customer_params)
if #customer.save
#customer.change_by(user_id)
flash[:success] = "Cliente cadastrado com sucesso!"
redirect_to customers_url
else
render 'new'
end
end
private
def customer_params
params.require(:customer).permit(:razao_social, :nome, :CPF_CNPJ,
:adress_id, :email_nota, :transporter_id, :observacao,
contacts_attributes: [:nome, :setor, :email])
end
Form
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for #customer do |f| %>
<%= f.label "Dados Básicos" %>
<div class="well">
<%= f.label :razao_social, "Razão Social" %>
<%= f.text_field :razao_social %>
<%= f.label :nome, "Nome" %>
<%= f.text_field :nome %>
<%= f.label :CPF_CNPJ, "CPF/CNPJ" %>
<%= f.text_field :CPF_CNPJ %>
<%= f.label :email_nota, "Email para nota" %>
<%= f.email_field :email_nota %>
<%= f.label :observacao, "Observações" %>
<%= f.text_area :observacao %>
</div>
<%= f.fields_for :contacts do |k| %>
<%= k.label "Contato" %>
<div class="well">
<%= k.label :nome, "Nome" %>
<%= k.text_field :nome %>
<%= k.label :setor, "Setor" %>
<%= k.text_field :setor %>
<%= k.label :email, "Email" %>
<%= k.email_field :email %>
</div>
<% end %>
<%= f.submit "Cadastrar Cliente", class: "btn btn-primary" %>
<% end %>
</div>
reject_if: lambda {|attributes| attributes['kind'].blank?}
No sign of :kind in your form or your customer_params
This might have something to do with it.
Other than that, if you need an add/remove relationship for contacts, check out the cocoon gem. If you only need one, then build that into your fields for:
<%= f.fields_for :contacts, #customer.contacts.first || #customer.contacts.build do |k| %>
The form will then be specific to a single instance of contact.
There's a way to store the Contact instance by getting this :0 hash?
(How do I access this hash? Is it "... :contacts_attribute[0]"?)
You don't need to access it, that's what the accepts_nested_attributes is for. The rest of your code looks ok so sort out the rejection issue at the top and come back if there are still problems, and post the log output - specifically the params hash for the request!

Rails 4, how to update a model field from a different controller?

I am trying to update an invoice fields, when checking out in the carts controller. These must be present when checking out, or it should fail. However, I can't get it to update, much less validate them.
Here is my code:
cart show view:
<div class = "row">
<div class = "col-lg-3 col-lg-offset-6 text-left">
<strong>Customer: </strong>
<%= collection_select(:invoice, :customer_id, #customers, :id, :full_name, {:prompt => 'Please Select'}, class: 'form-control') %>
</div>
<div class = "col-lg-3 ext-left">
<strong>Seller: </strong>
<%= collection_select(:invoice, :employee_id, #employees, :id, :full_name, {:prompt => 'Please Select'}, class: 'form-control') %>
</div>
<div class = "col-lg-12 text-right">
<%= form_tag carts_checkout_path, method: :post do |f| %>
<%= submit_tag 'Complete', class: 'btn btn-success' %>
<% end %>
</div>
</div>
carts controller:
class CartsController < ApplicationController
def show
#invoice = current_invoice
#invoice_products = current_invoice.invoice_products
#customers = Customer.all
#employees = Employee.all
end
def checkout
current_invoice.customer_id = params[:customer_id]
current_invoice.employee_id = params[:employee_id]
current_invoice.save
redirect_to current_invoice
end
end
current_invoice is the current session's invoice, related to the cart. It redirects correctly, but doesn't update.
in the invoices controller:
def invoice_params
params.require(:invoice).permit(:invoice_number, :customer_id, :invoice_date, :invoice_status_id, :employee_id, invoice_products_attributes: [:id, :invoice_id, :product_id, :price, :tax, :discount, :value])
end
Can anyone please help me in identifying where I am going wrong? Could it be my approach is not even valid?
Thanks in advance
The type of functionality you're after is considered "business logic" and should be implemented in the model and called from the controller.
You can define a method in a model:
class Invoice < ActiveRecord::Base
def update_invoice(cust_id, emp_id)
if self.update_attributes(:customer_id => cust_id], :employee_id = emp_id])
puts "Success!
else
puts "Failed to update record. Handle the error."
end
end
You can call my_method from carts_controller.rb like this:
def update
# all your regular update logic here
# replace the bit of code that saves the cart with something like this:
respond_to do |format|
if(current_invoice.update_invoice(params[:customer_id], params[:employee_id])
if(#cart.update(cart_params))
format.html { redirect_to #activity, notice: 'Activity was successfully updated.' }
format.json { render :show, status: :ok, location: #activity }
else
format.html { render :edit }
format.json { render json: #activity.errors, status: :unprocessable_entity }
end
end
end
Also, note the use of update_attributes rather than save. Bear in mind that update_attributes will return false if you run into any problems updating (e.g. one or more validations failed). Don't confuse update_attributes with the singular update_attribute which updates a single field and will not run validations.
Finally got it.
current_invoice.update_attributes(customer_id: params[:invoice][:customer_id], employee_id: params[:invoice][:employee_id])
Also in view, changed location of form_tag:
<div class = "row">
<%= form_tag carts_checkout_path, method: :post do |f| %>
<div class = "col-lg-3 col-lg-offset-6 text-left">
<strong>Cliente: </strong>
<%= collection_select(:invoice, :customer_id, #customers, :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
</div>
<div class = "col-lg-3 ext-left">
<strong>Vendedor: </strong>
<%= collection_select(:invoice, :employee_id, #employees, :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
</div>
<div class = "col-lg-12 text-right">
<%= submit_tag 'Completar', class: 'btn btn-success' %>
</div>
<% end %>
</div>
Could it be my approach is not even valid
Your approach is definitely valid, it's great that you're using sessions in this way.
I'd do it slightly differently:
#config/routes.rb
resource :cart, except: [:edit, :new, :create], path_names: { update: "checkout" }
This will give you the following paths:
#url.com/cart -> carts#show (here you can invoke a cart if one doesn't exist)
#url.com/cart/checkout #-> POST to "update" method in carts controller
#url.com/cart/ (method: :delete) -> DELETE to "destroy" cart (refresh)
--
#app/controllers/carts_controller.rb
class CartsController < ApplicationController
before_action :setup_cart
def show
#cart = current_cart #-> products called from this. I don't know how you're linking them
#customers = Customer.all
#employees = Employee.all
end
def update
#invoice = Invoice.find_or_create_by(id: current_card.id)
#invoice.update update_params
redirect_to cart_path
end
def destroy
current_cart = nil
redirect_to carts_path, notice: "Cart Cleared"
end
private
def setup_cart
current_cart ||= sessions[:cart]
end
def update_params
params.require(:cart).permit(:customer_id, :employee_id)
end
end
Now, to update the cart, you'll want to take note from MarsAtomic's answer. However it must be noted that naked params are not available in the model.
If you use update_attributes, or just plain update, you'll need to do the following:
#app/models/cart.rb
class Invoice < ActiveRecord::Base
has_many :products
belongs_to :employee
belongs_to :customer
#validations here
#callbacks here (what MarsAtomic refers to as business logic)
before_save :do_something, only: :update
private
def do_something
#something here
#params appended to current instance of object
#eg self.customer_id
end
end
I'd also go more succinct in your view:
#app/views/carts/show.html.erb
<div class = "row">
<%= form_tag cart_checkout_path, method: :patch do |f| %>
<% options = [["cliente", "customer"], ["vendedor", "employee"]] %>
<% options.each do |name, type| %>
<%= content_tag :strong, "#{name.titleize}:" %>
<%= collection_select :cart, eval(":#{type}_id"), instance_variable_get("##{type.pluralize}"), :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
<% end %>
<% content_tag :div, class: "col-lg-12 text-right" do %>
<%= submit_tag 'Completar', class: 'btn btn-success' %>
<% end %>
<% end %>
</div>

How to save data in rails with no form?

I am new to rails and I am just learning the basics.
This is my code on saving data:
app/controllers/employee_controller.rb
class EmployeesController < ApplicationController
def index
render json: #employees = Employee.all
end
def show
render json: #employee = Employee.find(params[:id])
end
def new
#employee = Employee.new
end
def create
#employee = Employee.new(employee_params)
#employee.save
redirect_to #employee
end
private
def employee_params
params.require(:employee).permit(:fname, :mname, :lname, :contactno, :address, :username, :password)
end
end
app/views/employees/new.html.erb
<%= form_for #employee do |f| %>
<p>
<label>First Name</label><br>
<%= f.text_field :fname %>
</p>
<p>
<label>Middle Name</label><br>
<%= f.text_field :mname %>
</p>
<p>
<label>Last Name</label><br>
<%= f.text_field :lname %>
</p>
<p>
<label>Contact No.</label><br>
<%= f.text_field :contactno %>
</p>
<p>
<label>Address</label><br>
<%= f.text_area :address %>
</p>
<br>
<p>
<label>Username</label><br>
<%= f.text_field :username %>
</p>
<p>
<label>Password</label><br>
<%= f.text_field :password %>
</p>
<br>
<p>
<%= f.submit %>
</p>
But, my goal is to save right away without the html form. (NO INPUT) Like when I visit a certain URL and the values are automatically saved in the database.
For a start, I would like to assign a constant value in every field just to see how it works.
Example,
fname='sample name'
mname='sampleMidName'
lname='sampleLastName'
and etc...
How can I assign those values right away after a certain URL/site is visited.
You start by adding a method to your controller
def update_fname
# get the parameters
fname = params[:fname]
# get the employee ID
id = params[:id]
# find the employee
#employee = Employee.find(id)
# update the employee
employee.update_attributes(fname: fname)
redirect_to #employee
end
Then, in your route, you add:
resources :employees do
get 'update_fname'
end
And you call the route, who should be http://localhost:3000/employees/{:id}/update_fname?fname={your_fname}
In your controller try something like:
class EmployeesController < ApplicationController
def custom
#employee = Employee.create(fname: "sample name")
end
end
and define proper route in config/routes.rb:
get "/custom" => "employees#custom"
When you enter proper url in your browser, like:
localhost:3000/custom
The Employee should be saved.
Good luck!

Updating multiple records massing assignment error

I'm working on a shows when a store was last visited. I want to be able to update multiple stores at once if they were all visited on the same day.
I think I have most of the code but I can't figure out how to get rid of the mass assignment error
Can't mass-assign protected attributes: date_visited(1i), date_visited(2i), date_visited(3i)
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"/yr8kLOyrTRGPfG1f/I5ilY/QB6GUx9IhQj6WiBaibM=",
"store_ids"=>["4",
"5"],
"visit"=>{"date_visited(1i)"=>"2012",
"date_visited(2i)"=>"11",
"date_visited(3i)"=>"14"},
"commit"=>"Save Visit"}
Model
class Visit < ActiveRecord::Base
attr_accessible :date_visited, :spent, :store_id
belongs_to :
end
Controller
def update_multiple
#visits = Store.find(params[:store_ids])
#visits.each do |visit|
visit.update_attributes(params[:visit])
end
flash[:notice] = "Updated products!"
redirect_to stores_path
end
View
<%= form_for :visit, :url => update_multiple_visits_path, :html => { :method => :put } do |f| %>
<ul>
<% #visits.each do |visit| %>
<%= hidden_field_tag "store_ids[]", visit.id %>
<% end %>
</ul>
<div class="field">
<%= f.label :date_visited %><br />
<%= f.date_select :date_visited %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<ol id="route">
<% #visits.each do |store| %>
<%= content_tag_for :li, store do %>
<%= "#{store.store} - #{store.address}" %>
<% end %>
<% end %>
</ol>
Most likely, you are missing the attr_accessible :your_model_attributes is this case, :visits_attributes on your activerecord model definition.
Also, your params should look like
{ visits =>
{ id_1 =>
{ :store_id
:attributes_for_visit_1 }
}
{ id_2 =>
{ :store_id
:attributes_for_visit_2 }
}
} # and so on....
# visits_controller.rb
def update_nultiple_visits
#visits = Visits.find(params[:visits].keys).each{|visit|visit.update_attributes!}
end
Add this to your Store model
attr_accessible :visits_attributes
accepts_nested_attributes_for :visits
And I'd suggest changing your controller to this:
def update_multiple
#stores = Store.find(params[:store_ids])
#stores.each do |store|
store.update_attributes(params[:visit])
end
flash[:notice] = "Updated products!"
redirect_to stores_path
end
Helper date_select generate three a select tags (for year, month and day).
You can concatenate its before updating attributes.
For example:
Date.civil(params[:visit][:date_visited(1i)].to_i, params[:visit][:date_visited(2i)].to_i, params[:visit][:date_visited(3i)].to_i)

Resources