Actually I need a big help, I'm using rails 5.1.4, I get stuck when rendering a partial with ActionCable and background Jobs. In my partial, I have a condition on current_user to check if current_user is the message owner and get his username with green color to make somes differents, so far display an update and delete button for each message he's the owner in the room conversations. But, if I refresh the page or remove current_user in the partial before rendering it works. However, I want that condition happens. Or, looking for a best way if there is one. THANKS IN ADVANCE.
Here is the error displayed in my terminal:
ERROR: ActionView::Template::Error: Devise could not find the warden::Proxy instance on your request environment.
Those are my files content.
The partial: _conversation.html.erb
<div id="message">
<% if conversation.user == current_user %>
<span style="text-align: center; color: green"><%= conversation.user.username %>
</span>
<% else %>
<span style="text-align: center; color: magenta"><%=
conversation.user.username %></span>
<% end %>
<p style="color: black"><%= conversation.body %></p>
</div>
Message_controller.rb
def create
conversation = #room.conversations.new(conversation_params)
conversation.user = current_user
conversation.save
MessageRelayJob.perform_later(conversation)
End
Room/show.html
<div id="conversations" data-room-id="<%= #room.id %>">
<%= render #room.conversations %>
</div>
MY JOB CLASS
class MessageRelayJob < ApplicationJob
queue_as :default
def perform(conversation)
ActionCable.server.broadcast "room_tchats_#{conversation.room.id}_channel",
conversation: render_conversation(conversation)
end
private
def render_conversation(conversation)
ConversationsController.render partial: 'conversations/conversation', locals: {conversation: conversation}
end
end
It can be happening when you try to get Devise current_user in some part of code being managed by action cable. like a background job to render comment or something else. you can resolve it by using something like following in your controller, since you cant access warden at a model or job level(according to my limited knowledge): (I call a job right after creation of my comment in CommentsController create action, which calls a private method to render a comment partial containing edit and delete button, for which current_user was required)
def create
#product = Product.find(comment_params[:product_id])
#comment = #product.comments.build(comment_params)
#comment.save!
gon.comment_id = #comment.id
gon.comment_user_id = #comment.user_id
ActionCable.server.broadcast "chat", comment: render_comment
render :create, layout: false
end
def render_comment
CommentsController.renderer.instance_variable_set(:#env, {"HTTP_HOST"=>"localhost:3000",
"HTTPS"=>"off",
"REQUEST_METHOD"=>"GET",
"SCRIPT_NAME"=>"",
"warden" => warden})
CommentsController.render(
partial: 'comments/comment_detail',
locals: {
product: #product,
comment: #comment
}
)
end
this will help you resolve warden issue, if you have used devise's current_user in that partial, it will give you the commentor user (as it should since that user initiated the rendering of partial). Now to solve this, if you have a front end framework you might need to fetch the current user from cookies in order to restrict some actions like edit/delete. but if you are working in pure rails the solution I came across is that you have to make a hidden field in the dom having current users id, and you will fetch that id for comparison in a script. you might need to access rails variables in javascript, for that you can use GON gem. I know this answer might contain much more than asked but I've searched alot and no where I found a satisfactory solution to this problem, feel free to discuss.
Related
I am trying to build a page to load order id but it threw back Couldn't find Order, can you help?
routes.rb get '/orders/cancellation', to: 'orders#cancel'
<h1>Preview & Cancel your order here</h1>
<div class="block">
<h1> Order <%= #order.name %></h1>
<div id="multiple-file-preview">
<ul id="sortable">
<div class="clear-both"></div>
</ul>
</div>
</div>
def cancel
paginate_versions PaperTrail::Version.where(order_id: order.id).order(created_at: :desc)
remote_versions_pagination
end
def order
#order ||= Order.find_by!(shopify_id: params[:id])
end
It might be due to the cancellation being taken as order id. You can verify it using debugger in show action.
If it is the case then you need to modify the routes file in such a way that orders/cancellation comes before the orders/:id i.e. the show action as rails will try to match the route in an up-down fashion and the request will be dispatched to the first matching route.
You should have instance variable defined in that specific controller action.
It will solve that issue
def cancel
#order ||= Order.find_by!(shopify_id: params[:id]) # Whatever you need to assign.. just make sure we have #order here pints to some order record
paginate_versions PaperTrail::Version.where(order_id:order.id).order(created_at: :desc)
remote_versions_pagination
end
I have a long running job that scrapes information from the internet (approx 2 mins) and I am having trouble with updating my pages/views with the new (additional) instances of the data that I scrape.
I thought it might be doable with a callback (on create/update) but I get an error saying I am redirecting too many times, so I figured I would most likely need some ajax and/or callback combination in order to achieve a continuous updating of the page. Im familiar with using ajax in order to update a section of the page when the user creates a post or comment but Im not sure how to do this coming from a background job.
I briefly toyed with using ActionCable (just opening a channel and somehow stream the data through there) but is there not an easier way to essentially update the page on a change in the database?
My current solution is to simply js reload the page for a while but that seems awefully primitive.
Controller
def create
#search = Search.new(search_params)
if #search.save
PreRunAllJob.perform_later(#search.title, #search.id)
respond_to do |format|
format.html { redirect_to search_path(#search) }
format.js # currently empty
end
else
render :new
end
end
def show
#search = Search.find(params[:id])
#jobs = Job.where(search_id: #search.id).order(quality: :desc)
end
Search Model
class Search < ApplicationRecord
has_many :jobs
end
Job Model
class Job < ApplicationRecord
belongs_to :search
end
searches/show.html.erb
<div class="container">
<%= render "jobs", remote: true %>
</div>
_jobs.html.erb
[...]
<% #jobs.each do |job| %>
<div class="card">
[...]
</div>
<% end %>
[...]
So just to quickly summarise: The long running background job creates an instance of 'Search' which has many instances of 'Job'. On the searches/show.html.erb I display cards, each holding information of a single instance of 'Job'. While the search is ongoing I'd like to update the view correspondingly (trickling in for instance).
What path could/should I take here? pure ajax? Callbacks?
I have ideas controller and static_pages controller. The latter has home action which displays all ideas and which i also use as root path.
I want the user be able to Edit the displayed ideas. So far i have this:
<% if #ideas.empty? %>
<p>Share your ideas! See what people think about it.</p>
<% else %>
<% #ideas.each do |idea| %>
<div class="panel panel-default">
<div class="panel-heading"><%= idea.name %></div>
<div class="panel-body"><%= idea.description %> <br>
<%= link_to 'Edit', edit_idea_path(idea.id) %>
</div>
</div>
<% end %>
<% end %>
I had an issue with an empty idea id which i solved by adding idea.id inside edit_idea_path
Now my question is, is that the proper, Rails way of doing it? In what other way can i fetch the idea object from this index page and use it in my ideas controller instead of static_pages controller?
I tried playing around with routing, but I have very vague understanding of it despite reading the guides and others code. I'd appreciate any insight about this matter.
First you need to understand that the requirement of your project defines what you should do in the code, whithout of concerning about the proper way to do something. You just need to follow the rails conventions.
http://guides.rubyonrails.org/active_record_basics.html#convention-over-configuration-in-active-record
Now, back to your question. You just need to create an action (that will handle a view) in your ideas_controller that will manage the edition of the data sended by de static_pages_controller, i will call it (just for example) edit_static_ideas and receive the data with params:
In your ideas_controller : app/controllers/ideas_controller.rb
def edit_static_ideas
#idea = Idea.find(params[:id])
end
Then you need to create the view in your views->ideas folder. An name it, just to continue my example i'll name it edit_static_idea.html.erb. And set the load of the data you get in #idea as a form or a form_for. Then you can submit that edited data and upload it into other action.
Then you have to configure your routes file and add
config/routes.rb
get 'edit_static_idea/:id', to: 'ideas#edit_static_idea', as: 'edit_ideas'
After that, if you run "rake routes" in your console (inside your rails project), you should see your new route (yay!)
Now you have to take the path in your route and use it in you static_pages_controller's view to redirect it to the edit_idea's view handle it by ideas_controller. And be sure that you also send the id of the selected item.
app/views/static_pages/home.html.erb:
<%= link_to 'Edit Idea', insert_your_edit_idea_obtainedinrakeroutes_path(id: idea.id) %>
At last, you only need to configure the form in your edit_static_idea.html.erb and assign it an upload/save route and redirect it to the view that you want.
for example:
In your routes file: config/routes.rb
patch 'save_edited_idea', to: 'ideas#save_edited_idea', as: 'save_edited_idea'
In your ideas_controller: app/controllers/ideas_controller.rb
def save_edited_idea
#idea = Idea.find(params[:id])
respond_to do |format|
if #idea.update(idea_params)
format.html { redirect_to the_view_that_you_want_path(id: #idea.id), notice: 'Data saved without problems.' }
else
flash.now[:alert] = "error"
format.html { render :offer }
end
end
end
I didn't wanted to be so detailed, because i wanted to help you to understand what you have to do. I hope it helps :P
Hi I'm making a rails app that uses Zendesk API calls. I have a controller that uses two classes I defined
class TicketsController < ApplicationController
require 'ticket_fields'
require 'ticket_search'
def getTickets
#search_fields = SearchFields.new(client)
#tickets = TicketSearch.new(client)
end
def search_tickets
#ddcustomvalues = [params[:customer_company_id], params[:study_id], params[:type_id], params[:system_id]]
#tickets.search_tickets(ddcustomvalues)
end
end
One class SearchFields uses the api to load values I want to filter tickets by into arrays. My view then uses these values to populate drop down lists.
The other class TicketSearch looks like this.
class TicketSearch
attr_reader :tickets, :text
def initialize(client)
#text = "query"
#tickets = Array.new
client.tickets.all do |resource|
#tickets << resource
end
end
def search_tickets(custom_search_fields)
querystring = "type:ticket+tags:"
custom_search_fields.each_with_index do |field, index|
unless field == ""
if index ==0
querystring += "#{field}"
else
querystring += " #{field}"
end
end
end
#text = querystring
end
end
What I want to happen in my view is when a button is pressed it changes the value of #text to the querystring generated by the drop down list options that were selected. I'm currently doing this for testing to see if my querystring is correct and the button works. What I eventually want it to do is send the querystring to the ZenDesk Server and returns the tickets I filtered for. the #tickets array would then be replaced with the filtered tickets the server returned. Currently my button code looks like this.
<%= button_to 'Search', :action => 'search_tickets' %>
with all the route code I've tried I either get an error upon starting the page. Or when I press the button nothing happens and the #text being displayed in my view remains "query". Can someone help explain what I need to do I don't quite understand how routes work.
==================================================================================
Hey so I made the changes you suggested and did some reading up on AJAX and js and I think I'm almost at the answer my view now looks like this
<div id="test" >
<%= render partial: 'text', locals: { text: #tickets.text} %>
<div id="test" >
and I created a partial _text file that looks like this
<p> Query: <%=text%> </p>
and a js file search_tickets.js.erb
$("#test").html("<%= escape_javascript(render partial: 'text', locals: { text: #tickets.text } ) %>");
any idea what may be going wrong everything loads up okay but the text remains the same in the partial i set up when i hit the button still
the console outputs this after the button is hit
ActionController::RoutingError (No route matches [POST] "/tickets/search_tickets"):
so I guess it may actually be a routing error my route looks like this
resources :tickets do
collection do
put :search_tickets
end
end
and the form tag calling the path looks like this
<%= form_tag search_tickets_tickets_path, remote: :true do %>
<table>
<tr>
<td align = "left" valign="middle"> <font size = 4> Customer Company </font> </td>
<td align = "left" valign="middle">
<%= select_tag "customer_company_id", options_for_select(#search_fields.customer_companies), :prompt => "Select One" %>
</td>
</tr>
......
<tr>
<td> </td>
<td align = "left" valign="middle">
<%= submit_tag "Search" %>
</td>
</tr>
</table>
<% end %>
==================================================================================
(Update)
I think I fixed my last problem by changing my form tag to this
<%= form_tag search_tickets_tickets_path(#tickets), method: :put, remote: :true do%>
however now I get this error from the terminal after I hit the button
NoMethodError (undefined method search_ticket' for nil:NilClass):
app/controllers/tickets_controller.rb:15:insearch_tickets'
how would I pass #tickets as a parameter through my route because clearly its not accessible by search_tickets right now as its giving a nil class error.
Variables
when a button is pressed it changes the value of #text to the querystring generated
It looks to me like you're confused with the stateless nature of Rails - in that, just because a view has been rendered doesn't mean the values / variables are still available for use.
It was mentioned in the comments that it seems you're basing a lot on experience with other frameworks / programming patterns. The best way to describe your solution is that Rails has to "refresh" all your variables / values each time it processes a request; consequently meaning that if you send a button request - you'll have to perform the request as if it were the first one
Ajax
The bottom line is that you need to use an ajax request to pull this off.
To do this, you'll be be best creating a form (not just a button_to), as this will give you the ability to send as many params as you want. You should use form_tag:
#config/routes.rb
resources :tickets do
collection do
get :search_tickets
end
end
#view
<%= form_tag tickets_search_tickets_path, remote: :true do %>
... #-> fields for your params
<%= submit_tag "Search" %>
<% end %>
This will give you the ability to define the following in your controller:
#app/controllers/tickets_controller.rb
Class TicketsController < ApplicationController
def search_tickets
#ddcustomvalues = [params[:customer_company_id], params[:study_id], params[:type_id], params[:system_id]]
#tickets.search_tickets(ddcustomvalues)
respond_to do |format|
format.js #-> loads /views/tickets/search_tickets.js.erb
format.html
end
end
end
#app/views/tickets/tickets_search.js.erb
//JS here to manipulate your original page
Requests
The bottom line here is that if you want to "manipulate" your view without refreshing, unlike "native" application frameworks, where you can rely on a persistent state, with Rails, you basically have to construct the request from scratch (IE passing all the params required for the method to run)
In my app, I've got a little box that appears on every page, checking on the status of requests made by the user. If a request is accepted at any time, then the user should automatically be taken to a certain page. This is my code so far:
<% offersMade.each do |w| %>
<% if w.accepted == true %>
<% redirect_to offer_path(:email => "email#gmail.com") %>
<% end %>
<% end %>
But I'm getting this error:
undefined method `redirect_to' for #<ActionView::Base:0x1042a9770>
Is it not possible to user redirect_to in a view? If not, is there something else I can use? Thanks for reading.
redirect_to is a method of ActionController::Base Class so you can not use it in ActionView.
You can try following
<% if w.accepted == true %>
<script type="text/javascript">
window.location.href="/logins/sign_up" // put your correct path in a string here
</script>
<% end %>
Edited for the email parameter
window.location.href="/logins/sign_up?email=<%= w.email %>"
Sorry i don't know if there is anything in ruby for that.
If you want to use redirect_to from view , do this method:
syntax : <%controller.redirect_to path %>
Example:<% controller.redirect_to users_profile_path %>
The code in the view could be moved into the controller which would make redirect_to available.
class ApplicationController < ActionController::Base
before_action :check_for_accepted_offer
def check_for_accepted_offer
if Offer.any? { |o| o.accepted }
redirect_to offer_path(:email => "email#gmail.com")
end
end
end
If the OP wanted to immediately change the URL when an offer is accepted then none of the Ruby code shown in the answers would help because it is only evaluated when the page is loaded. The OP would need to setup some kind of polling or push strategy to alert the browser when an offer has been accepted and then use the JavaScript redirect scheme posted in another answer:
window.location.href="/logins/sign_up?email=<%= w.email %>"
Although, this does not answer the question: "How can I use redirect_to in a view", I think this answer would ultimately have been more useful to the OP. I stumbled across someone using this answer to redirect to another page, when the redirect should have been performed in the controller.
redirect_to is not a method of ActionView. Its a method of ActionController. You can probably use Javascript window.location.href on page load or some other event to take your user to another page.
Yes, you can call controller.redirect_to from your view to get what you want without having to render the whole response and then use javascript on the client to make a new request.
In your example, this would look like:
<% offersMade.each do |w| %>
<% if w.accepted == true %>
<% controller.redirect_to offer_path(:email => "email#gmail.com") %>
<% end %>
<% end %>
Note that this controller.redirect_to will not break out of your loop, so you will probably want to break, and you'll probably want to make sure that the rest of your view is only conditionally rendered if you didn't redirect.
(Disclaimer: I don't necessarily condone this technique. You would be better off doing this in your controller or helper, as others have mentioned.)