Rails 4 search from "home page" to other models/controllers - ruby-on-rails-4

UPDATE 4-6-2015:
Double checked here:
https://github.com/activerecord-hackery/ransack
to make sure I had everything right, aside from a few hiccups I think I do. I am not clear how or what automagic makes the f.submit whisk the user away to that model index with nothing in that model filled out, and the index action taking presidence (hence after doing a search takes me to the index view of the groups (what I am searching on) and shows everything from db as if the Group.all was called ignoring the search string (that I can see in the address bar) completely.
Really not sure what I am missing I am sure it something minusculey stupid on my part.
EDIT/UPDATE: So i got most of this working, the search screen is going to that controller. I am not sure what automagic is causing that, I thought my respond_to could trap and send back json so I could this with an ajax call and update the table that is in the main page. THEN if they click on that table it takes them to the edit form of that particular item (i.e. the edit of the correct model).
My visitor_controller.rb:
def index
#search = Group.ransack(name_cont: params[:q])
#groups = #search.result
respond_to do |format|
format.html
format.js
end
end
Adding the routes file too:
Rails.application.routes.draw do
resources :groups
get 'visitors', to: 'visitors#index'
root to: 'visitors#index'
end
So groups is a controller/model that I am trying to search on (one of many, will use the example in the answer to do many when I get the one just working first.)
So I am building an app, I found a "simple search" tutorial for rails 4:
http://www.stefanosioannou.com/rails-4-simple-search-form/
But I think that this assumes you to search from the view/model and all that of your controller. I.e.
I am in the groups view, I want to search my groups all through that controller.
Really what I am doing and finding so hard to do is have a welcome page, that has three search bars. Each bar searches a different model/view/controller.
The welcome page is called visitors and has its own controller, and view just no model.
So my visitors controller has this:
class VisitorsController < ApplicationController
def index
#groups = Group.search(params[:gsearch])
respond_to do |format|
format.html
format.js
end
end
end
I was just trying to get one search bar working for my Group model, so in my Visitors index.html.erb:
<div class="span12">
<h3>Welcome IPA of NM</h3>
<!-- search bar here -->
<% form_tag group_path, :method => 'get' do %>
<p>
<%= text_field_tag :gsearch, params[:search] %>
<%= submit_tag "Search Groups", :name => nil %>
</p>
<% end %>
</div>
This fails though as it says
No route matches {:action=>"show", :controller=>"groups"} missing required keys: [:id]
so here is my groups controller:
class GroupsController < ApplicationController
before_action :set_group, only: [:show, :edit, :update, :destroy]
def show
end
private
# Use callbacks to share common setup or constraints between actions.
def set_group
#group = Group.find(params[:id])
end
end
I am sure somehow the form code in the visitors index.html.erb is working with that group_path which means it is trying to do a show but has no ID yet when just starting? I really am lost, new to rails4 and not sure what I am missing here. I am guessing the convention is do all things from your model/view/controller not a cross views as I am trying to do here? So something I have to do extra to get around the convention or I am doing the whole thing wrong from the get go?

I would highly recommend Ransack for search queries in rails. You can view it here > https://github.com/activerecord-hackery/ransack and you can also watch an awesome rails cast by Ryan Bates over at http://railscasts.com/episodes/370-ransack
Ransack works across the board, you could even search all 3 models from the same form.
Using ransack, you would simply use:
<%= search_form_for #q do |f| %>
# Search if the models name field contains...
<%= f.label :name_cont %>
<%= f.search_field :name_cont %>
<%= f.submit %>
<% end %>
Then the search controller processes this into #results:
def index
#q = params[:q]
#search = Model.search(name_cont: #q)
#results = #search.result
end
Then you would loop through the results on your show/index page.
<% #results.each do |result| %>
<%= result.name
<% end %>
This would show all of your results on one search page. If you wanted to search multiple models, you can either have multiple forms assigning a different instance variable for each one eg: #q1 #q2 #q3 or, you could search them all from one model by using different instance variables for the search and results.eg
def index
#q = params[:q]
#firstsearch = User.search(username_cont: #q)
#secondsearch = Post.search(title_or_description_cont: #q)
#users = #firstsearch.result
#posts = #secondsearch.result
end
Remember to add the routes =)
get 'search', to: 'search#index'

Related

Accessing class variables in views

What is the correct syntax for storing something in my class variable from a view ? Let's say that this is the code:
Controller:
class FooController < ApplicationController
##myvar = "I'm a string"
*
*
*
methods
*
*
*
end
Form:
<%= form_for(#something) do |f| %>
<%= f.text_field :myvar %>
<%= f.submit %>
<% end %>
This is just an example of a form as it is not functional, I cannot find what the correct syntax is for accesssing ##myvar from the view. Thanks !
as on request i edited, including attr_accessor
Rails way. Ill just fly over, hope you get it. you defenitely need to read more introductions to rails and its concept.
you have a rails-model, lets call it Animal
class Animal < ActiveRecord::Base
attr_accessor :non_saved_variable
end
this is having a database-table, lets say in this table we store race, name and age.
now we need a controller, to create/edit/update/delete animals
class AnimalController < ActionController::Base
def new
# this creates a new animal with blank values
#animal = Animal.new
end
end
now you need to go into your routes.rb and create a route for animals
resources :animal
this will create all (restful) routes for every actions of an animal.
now you need to have your template to render a form
form_for is a rails helper, to create a form, associated with the #animal (which is a new Animal). You pass |f| into the block, so with f you can access the form
=form_for #animal do |f|
then you can go for each field you need to call another rails helper
you can also access attr_accessors.
=f.text_field :race
=f.text_field :name
=f.text_field :age
=f.text_field :non_saved_variable
by that you get the stuff
dont fortget f.submit because your form needs a submit button
if you now click on your button, the form will be posted to the create method of rails. so you need to bring it into your controller
def create
# create a new animal with the values sended by the form
#animal = Animal.new params[:animal]
# here you can access the attr_accessor
#animal.do_something if #animal.non_saved_variable == "1337"
if #animal.save
# your animal was saved. you can now redirect or do whatever you want
else
#couldnt be saved, maybe validations have been wrong
#render the same form again
render action: :new
end
end
i hope that gave you a first insight of rails ?!
DO NOT DO THIS
You can get or set class variables from any class by:
<%= FooController.class_variable_get(:##myvar) %>
<%= FooController.class_variable_set(:##myvar, 'value') %>
This is probably not what you want. Do not do it. What are you trying to achieve?
DO THIS INSTEAD:
If it's a variable that you want to be available to all actions in that controller consider an instance variable set in a a before filter:
class FooController < ApplicationController
before_filter :set_var
private
def set_var
#my_var = "I'm a string"
end
end
Then in your view, just call <%= #my_var %>

Whats the best way to create a self-referential association in Rails 4?

In my project I am dealing with objects called Workflows. Every Workflow can have many subflows (Workflows that it calls). At the same time every workflow (Including ones with sub flows) may also act as a Superflow that is calling the sub-flows and may have many Superflows that call it as a sub flow.
I am currently trying to do this by using a join table through a model called FlowRelation.
I have followed every tutorial I can find and read every article I can find and I still can't get this working.
Workflow Model:
class Workflow < ActiveRecord::Base
belongs_to :superflow, :class_name => 'Workflow'
has_many :subflows, :class_name => 'Workflow', :foreign_key => 'subflow_id'
end
FlowRelation Model:
class FlowRelation < ActiveRecord::Base
belongs_to :workflow
belongs_to :flow, :class_name => "Workflow"
end
Inside my workflows edit.html.erb (Using SimpleForm)
<%= simple_form_for #workflow, :html => { :class => 'form-horizontal', :multipart => true } do |f| %>
<%= f.input :workflow_id %>
<%= error_span(#workflow[:workflow_id]) %>
<%= f.association :subflows, :include_blank => t('.select_workflow') %>
<%= error_span(#workflow[:subflow_id]) %>
<%= f.association :superflow, :include_blank => t('.select_workflow') %>
<%= error_span(#workflow[:superflow_id]) %>
And my Workflow Controller is pretty straightforward
def new
#workflow = Workflow.new
end
def edit
#workflow = Workflow.find(params[:id])
#workflow.save
end
Currently, when I attempt to save new sub_flows to an existing or new Workflow, nothing ever gets saved. Nothing shows up in the database, and I see no error.
I also have a FlowRelationsController
class FlowRelationsController < ApplicationController
before_action :set_flow_relation, only: [:show, :edit, :update, :destroy]
def create
#flow_relation = current_workflow.flow_relations.build(:flow_id => params[:flow_id])
if #flow_relation.save
flash[:notice] = "Added flow."
redirect_to root_url
else
flash[:error] = "Unable to add flow."
redirect_to root_url
end
end
If I attempt to create an add sub_flow button to my workflow index with
<%= link_to "Add Subflow", flow_relations_path(:flow_id => workflow), :method => :post %>
My flow_relations table remains empty as well. and I get the following error:
undefined local variable or method `current_workflow' for #
<FlowRelationsController:0x007f70154921e0>
I know its a bunch of pieces, but I'm completely stuck, been hitting my head against a wall on this one for days. Anyone see something I'm missing here? I've been trying to implement something similar to what was described on RailsCasts here with no success: http://railscasts.com/episodes/163-self-referential-association?view=asciicast
Please HELP!
Thanks
Without digging into any of the other self-referential model stuff you're doing here, the error you see is a controller error. It doesn't know about 'current_workflow'. My guess is that in your 'set_flow_relation' before callback, you are setting #current_workflow. I cannot be sure without seeing the rest of your controller code.
Once that is fixed, please post any follow up problems you have.
I was misunderstanding a couple different things. I thought I'd answer my own question to help bring clarity to any others who may be in the same boat.
1st major issue I had above was in the FlowRelationsController. I was using an object called current_workflow which didnt exist. This happened due to following the only video i could find online about this topic at: http://railscasts.com/episodes/163-self-referential-association. This is a create resource, but is not entirely the same as what I was trying to accomplish. The current_workflow object wasnt being created in my case because I was starting from an index and not a workflow details section of my application
Now, as to how to accomplish my original goal. Here is what I ended up with:
Workflow Model
has_many :flow_relations
has_many :subflows, :through => :flow_relations
FlowRelation Model
self.primary_key = :workflow_id
belongs_to :workflow
belongs_to :subflow, :class_name => 'Workflow'
Inside of my simpleForm used for creation and editing of workflows. (This was a pretty key piece to understand. - doing the association to subflows with a collection of all workflow objects.
<%= f.association :subflows, collection: Workflow.all, prompt: "Select Subflows" %>
<%= error_span(#workflow[:subflow_id]) %>
The Workflow controller stayed mostly the same with one key difference due to Rails 4. - I had to change the workflow_params to permit!. I did this on both the Worfklow Controller and the FlowRelations controller. Not sure I understand still how this should be configured for better security, but in this particular application, security is not an issue as its to be run internally.
def new
#workflow = Workflow.new
end
def edit
#workflow = Workflow.find(params[:id])
#workflow.save
end
def workflow_params
#params.require(:workflow).permit(:workflow_id, :flow_relations, :re_workflow_id, :name, :description, :superflow_id, :client_id, :variable_id, :required_variable_id, :flow_file)
params.require(:workflow).permit!
end
I removed the add subflow button, since now it could be done through the standard create or edit actions
Its a bunch of stuff, but basically, the end result is I now have a Workflow object that can contain mnay subflows all of which can be a member of many Workflows, all within a single Workflow class and a FlowRelations class for handeling the join.

trying to link image to specific model id in rails

I'm having a routing issue with an image. In my app I have images of items on the home page. I would like them to link to their image page.
Here is what my items controller looks like:
class ItemsController < ApplicationController
def show
#item = Item.find(params[:id])
end
end
This is what I have in my routes:
Rails.application.routes.draw do
resources :items
end
And this is what I have in the item partial:
<%= link_to(image_tag(item.image.url(:thumb)), item_path(:id)) %>
What I expected after reading the rails routing guide was that this would link to the item page for that image. Here is their example:
photo_path(:id) returns /photos/:id (for instance, photo_path(10) returns /photos/10)
I should also add that this is in my home page controller:
class StaticPagesController < ApplicationController
def home
#items = Item.where.not(category_id: 1)
end
However, that is not working. I've tried several different things, but all produce errors. Is there a simple way to do this?
The normal way to do what you want is this:
<%= link_to item_path(item) do %>
<%= image_tag(item.image.url(:thumb)) %>
<% end %>
You can just pass the instance of the item to item_path and also if you have complicated html for a link, it is usual to put it in a block for the link as shown here (with link_to something do).

Rails 4 update attribute (belongs_to) with click on link ( within dropdown list on show page)

In my app I have Issue model, which belongs_to Status model. What I need to do is to have dropdown list of links (or spans, doesn't matter) with Statuses Id's on Issue show page, so I could change status clicking on this (could be non-ajax and ajax).
I am rather new to Rails, so I do not know how to implement this, was thinking of few ways to do this, but non seems to be working.
Because you're new, I'll outline what I'd do for you (hopefully it will help):
#config/routes.rb
resources :issues do
get "change_status/:status_id", to: "issues#change_status"
end
#app/models/status.rb
Class Status < ActiveRecord::Base
has_many :issues
scope :not, ->(id) { where('statuses.id != ?', id) }
end
#app/controllers/issues_controller.rb
def show
#issue = Issue.find params[:id]
#statuses = Status.all.not(#issue.id)
end
def change_status
issue = Issue.find params[:id]
issue.status = params[:status_id]
issue.save if issue.status_id_changed?
redirect_to issue
end
#app/views/issues/show.html.erb
<%= #statuses.each do |status| %>
<%= link_to status.title, issue_change_status_path(#issue, status) %>
<% end %>
There's obviously some stuff to explain - if you need me to do that, let me know and I'll give you the details!
You can simply use the link_to rails helper like in the following code:
link_to "Change Status", my_path_to_update_status, remote: true, method: :put
This will send an AJAX PUT request (thank to the remote: true) to your my_path_to_update_status.
Then, you just have to update the status in the action corresponding to the path specified in the link_to helper tag.
More information about the link_to rails helper here

How to render a view inside of another view (rails 4)

Hi I want to preface this by saying that I am new to coding.
I have an issue that I believe can be solved in two ways
A. by rendering a partial
B. by updating the controller
( I can totally be wrong but these are what I suspect lol)
I have two controllers/views "reviews" and "logs". and I want them to both appear on the same page.
How can I do this?
I tried rendering a partial but I alway get an error.
and I tried the piece of code below:
which made my reviews show up on the page, but when I add
#log = #user.logs.all
to it, it doesn't pull all the logs to the page like it does for the reviews.
def show
#user = User.find_by_name(params[:id])
if #user
#reviews = #user.reviews.all
render action: :show
else
render file: 'public/404', status: 404, formats: [html]
end
end
First things first. Views refer to actions in controllers. So there can be several views for each controller or even none.
So, if you want to render #reviews and #logs on the same page you should first instantiate both instance variables in the same action and then render both partials in the same action.
How do you do that? Easy.
First you got to the controller you just showed and edit that show action.
def show
# You can set the variable in the if-clause
# And you also need to use static finders with a hash as an argument in Rails4
if (#user = User.find_by(name: params[:id]))
#reviews = #user.reviews.all
#logs = #user.logs.all
# You don't need to call render explicitly
# if you render the view with the same name as the action
else
render file: 'public/404', status: 404, formats: [html]
end
end
Second: you go to your /app/views/reviews/show.html.erb template and put both partials there like this (this is just an example, adjust your markup to fit your needs).
<h1> Reviews and Logs</h1>
<div id="reviews_part">
<%= render #reviews %>
</div>
<div id="logs_part">
<%= render #logs %>
</div>
Now create 2 new partials /app/views/reviews/_review.html.erb and /app/views/logs/_log.html.erb and put all the needed markup there (use regular variables review and log to adress the repeating objects). Rails will automaticaly repeat those partials as many times as needed.
Or you can explicitely call the partial render
<div id="reviews_part">
<% #reviews.each do |review| %>
<%= render review %>
which is the same as
<%= render partial:"reviews/review", locals:{review:review} %>
<% end %>
</div>
Here is the way of rendering partials into views in HAML:
=render :partial => "header"
%h2 Hello World
=render :partial => "footer"
Every partial you render this way, has to be created within the same folder.
Each partial's name has to begin with an underscore (_). This should be the view's directory:
- home
- index.html.haml
- _header.html.haml
- _footer.html.haml