Getting a value from an array of hashes in Ruby - ruby

I have an array:
address => [{"data"=>{"zip"=>"60628", "state"=>"IL", "city"=>"chicago", "street"=>"123 Main St. Apt 2"}, "primary"=>true}]
I want to show each piece in the right format on my view like:
123 Main St. Apt 2
Chicago, IL 60628
I am having trouble getting the logic correct to show this.
Where should this logic go, as a method in my model or in the actual view?
I tried:
<% #applicant.address.map{|x| x[:data]}.each do |address| %>
<%= address.street %><br><%= address.city %>, <%= address.state %> <%= address.zip %> <br>
<% end %>
but got:
=> [{"zip"=>"60628", "state"=>"IL", "city"=>"chicago", "street"=>"123 main st Apt 2"}]

The map gives you a new array and if you use hashes you need to access the values with the hash[:key] or hash["key"] operator, try this:
<% #applicant.address.each do |address_hash| %>
<% address = address_hash["data"] %>
<%= address["street"] %><br><%= address["city"] %>, <%= address["state"] %> <%= address["zip"] %> <br>
<% end %>
But if I were you , I'd put the data in an array of structs, it's more readable.
In your model or helper:
address = Struct.new(:street, :city, :state, :zip)
address.street = ..
...
#addresses << address
etc
In your view:
<% #addresses.each do |address| %>
<%= address.street %><br><%= address.city %>, <%= address.state %> <%= address.zip %> <br>
<% end %>

For your first question something like this should suffice:
#!/usr/bin/env ruby
require 'ostruct'
address = [
{
'data' => {
'zip' => '60628',
'state' => 'IL',
'city' => 'chicago',
'street' =>'123 Main St. Apt 2'
},
'primary' => true
}
]
class AddressPresenter
def initialize(address)
#address = OpenStruct.new(address)
end
def to_s
"#{#address.street} \n#{#address.city} #{#address.state} #{#address.zip}"
end
end
address.each do |a|
puts AddressPresenter.new(a['data'])
end
Now, for the second question, I would implement a Presenter class whose only goal is to "present" the data in whatever format you need in the view, this allows you to keep your views with 0 presentation logic. So in this case the class AddressPresenter could be reworked to return in the to_s the right HTML you need in your view. And you would do something like:
<% #applicant.address.each do |address| %>
<%= AddressPresenter.new(address['data']).to_s %>
<% end %>
By having a separated presenter class, you get the following benefits:
Your models don't worry about how to present/render stuff because that's not their job
You can test this presenter to confirm it returns whatever you're expecting in the view
Your views don't contain any complicated logic

Related

How to save many items on one form rails?

I need to save many items to Cart on form, user enter quantity one form, and selected items goes to db, but now save only first entered quantity of item. Why?
my form
<%= form_for #cart_item do |f| %>
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, :value => item.id %>
<%= f.text_field :qty %>
<% end %>
<%= f.submit %>
<% end %>
And controller
cart_items_controller.rb
class CartItemsController < ApplicationController
before_action :set_cart, only: [:create]
def create
#cart_items = CartItem.create(cart_items_params)
#cart_items.cart_id = #cart.id
if #cart_items.save
redirect_to :back
else
render root_path
end
end
private
def cart_items_params
params.require(:cart_item).permit(:id, :qty, :item_id, :cart_id)
end
def set_cart
#cart = Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
#cart = Cart.create
session[:cart_id] = #cart.id
end
end
There are a few problems here. I'll give you a little bump:
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, :value => item.id %>
<%= f.text_field :qty %>
<% end %>
For each CartItem, this is going to create an input like this
<input name="qty">
This is problematic because only one (the last one in the DOM) will be submitted. You need to research fields_for and incorporate that into your loop in order to get unique names for each Item in the form.
This same issue follows through into your controller
def cart_items_params
params.require(:cart_item).permit(:id, :qty, :item_id, :cart_id)
end
This is going to look for a single :id, :qty, :item_id, and :cart_id, when in reality you're looking to accept multiple :item_id and :qty fields. You need to research Strong Parameters with nested has_many associations.
Finally you have this
#cart_items = CartItem.create(cart_items_params)
which is going to attempt to create a single CartItem when you're really trying to create multiple items and associate them back to the Cart. You need to research accepts_nested_attributes_for as well as more generally "rails form save has_many association". It's a widely covered topic here on SO and elsewhere.
I do this:
def create
#cart_items = params[:cart_items]
#cart_items.each do |c|
#cart_item = CartItem.new(c)
if #cart_item.qty.present?
#cart_item.cart_id = #cart.id
#cart_item.save
end
end
and form
<%= form_tag cart_items_path do %>
<% #cart_items.each do |cart_item| %>
<%= fields_for "cart_items[]", cart_item do |f| %>
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, value: item.id %>
<%= f.text_field :qty %>
<% end %>
<%= f.submit %>
<% end %>
<% end %>
<% end %>

only showing button to index action when there are objects listed in the index(in the database)

I have a button that links to the projects updates index action through all_project_updates_path i set in my routes. Here is my button code:
<%= button_tag type: "button", :class => "radius" do %>
<%= link_to 'Project Updates', all_project_updates_path(#project), :style => "color: white" %>
<% end %>
I want this button to only be visible when there are project updates in the database. Otherwise I want this button to dissapear. I tried:
<% if all_project_updates_path(#project) != nil %>
<%= button_tag type: "button", :class => "radius" do %>
<%= link_to 'Project Updates', all_project_updates_path(#project), :style => "color: white" %>
<% end %>
<% end %>
And also
<% if #updates != nil %>
<%= button_tag type: "button", :class => "radius" do %>
<%= link_to 'Project Updates', all_project_updates_path(#project), :style => "color: white" %>
<% end %>
<% end %>
but that doesn't seem to work. Looking for a simple explanation as I am a relative beginner with ruby.
This is the route:
get 'all_project_updates/:id' => 'project_updates#index', as: 'all_project_updates'
This is my projects controller(show action)
def show
#project = Project.find(params[:id])
#comments = Comment.all.where(:project_id => #project.id)
#updates = ProjectUpdate.all.where(:project_id => #project.id)
end
And this is my project updates controller index action:
def index
#projectUpdates = ProjectUpdate.where(:project_id => params[:id])
respond_to do |format|
format.html
end
end
You’re examining a path helper, all_project_updates_path, when you need to be querying a model object. The all_project_updates_path call is a helper to return a path for linking between web pages.
all_project_updates_path(#project) # => /all_project_updates/1
So you’re really asking:
'/all_project_updates/1'.nil? # => false
Because it’s just a string, it won’t be nil.
Instead, you should be examining the project_updates directly. I’m not sure how your models are related, but assuming that a Project has_many :project_updates, try this:
if #project.project_updates.any?
That will return true if a #project has any updates.
Beyond your immediate question, I would recommend considering whether nested resources are a better fit for this usage. You could declare your routes like this:
resources :projects do
resources :project_updates
end
Then you would get project_project_updates_path(#project) and no longer need your custom route that pretty much duplicates that functionality.
Try this:
<% if #updates.any? %>
<%= button_tag ... %>
<% end %>
#updates is an empty collection (ActiveRecord_Relation to be precise) of ProjectUpdate objects if no records were found, it's not nil.

RoR: how can I search only microposts with a certain attribute?

Right now in app/views/microposts/home.html.erb I have..
<% form_tag purchases_path, :method => 'get', :id => "products_search" do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<% form_tag sales_path, :method => 'get', :id => "sales_search" do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
and then in micropost.rb I have
scope :purchases, where(:kind => "purchase")
scope :sales, where(:kind => "sale")
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
else
scoped
end
end
and then finally in the microposts_controller.rb I have
def home
#microposts=Micropost.all
#purchases=#microposts.purchases
#sales=#microposts.sales
end
Right now I am getting an error saying undefined local variable or method `purchases_path' and it does the same for sales_path.
What I want to be able to do is search only some of the microposts instead of all of them. In my micropost table I have a column called kind which can be either "purchase" or "sale". How can I change these three pieces of code so that one search searches through and displays results for only those microposts with the kind "purchase". And then the other searches through and displays results for only those microposts with the kind "sale"
this question (on another post) has a bounty with 50 rep at RoR: how can I search only microposts with a specific attribute?
You might try this.
Your model:
class Micropost
# ...
scope :purchase_only, where(:kind => "purchase")
# ...
def self.search(search)
if search
self.purchase_only.find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
else
self.purchase_only
end
end
end
But, this stuff looks very strange to me.
E.g.: You should remove the .find(...), this finder will be deprecated with Rails 4.

How to pass name of form_for's method to partial in rails?

Good day! Little newbee question:
let's say I want to generate fields of form_for helper of special appearance. Thus to not to repeat complex code to generate many fields of model, I render some partial and pass there parameters. There is a reference to form object amongst them. Question is what pass to render method to call in the partial necessary method of form object? For example I want to generate this time email_field:
<%= form_for #order do |f| %>
<%= render :partial => 'form_field_special', :locals => {:form => f, :type => :email_field, :labelcaption => "SpcName"} %>
<% end %>
and partial itself:
<div class="control-group">
<%= f.label :name, <%= labelcaption %> , :class => "control-label" %>
<div class="controls">
<%= f.type :name %> # ??????
</div>
</div>
thank you guys!
Use ruby power. Object#send method:
<%= form_for #order do |f| %>
<%= render :partial => 'form_field_special', :locals => {..., :field_type => 'email_field'} %>
<% end %>
<%= f.send(field_type, :name) %>
The following method proved as the fastest way to invoke ruby object's function by its name:
m = form.method(name_of_the_method)
m.call(parameters)

How can I have custom result with autocomplete

In view:
<%= text_field_with_auto_complete :vendor, :number, {}, {:with => "'vendor[number]=' + $('vendor_number').value"} %>
In Controller:
auto_complete_for :vendor, :number do |vendors, params|
vendors.vendor_company_filter(params[:company_id])
end
It works fine, gives list of vendor number as a result
but I need "vendor name + vendor number" in a result list, how can I achieve this..??
please help..
found an easy way,
in Controller
def auto_complete_for_vendor_number
#vendors = Vendor.find_by_number(number)
render :partial => 'auto_complete_for_vendor_number'
end
in _auto_complete_for_vendor_number.html.eb partial:
<ul>
<% #vendors. each do |vendor| %>
<li><%= vendor.name+'('+vendor.number+')' %></li>
<% end %>
</ul>
It worked fine :)

Resources