Rails routing to XML/JSON without views gone mad - ruby-on-rails

I have a mystifying problem. In a very simple Ruby app i have three classes: Drivers, Jobs and Vehicles. All three classes only consist of Id and Name. All three classes have the same #index and #show methods and only render in JSON or XML (this is in fact true for all their CRUD methods, they are identical in everything but name). There are no views. For example:
def index
#drivers= Driver.all
respond_to do |format|
format.js { render :json => #drivers}
format.xml { render :xml => #drivers}
end
end
def show
#driver = Driver.find(params[:id])
respond_to do |format|
format.js { render :json => #driver}
format.xml { render :xml => #driver}
end
end
The models are similarly minimalistic and only contain:
class Driver< ActiveRecord::Base
validates_presence_of :name
end
In routes.rb I have:
map.resources :drivers
map.resources :jobs
map.resources :vehicles
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
I can perform POST/create, GET/index and PUT/update on all three classes and GET/read used to work as well until I installed the "has many polymorphs" ActiveRecord plugin and added to environment.rb:
require File.join(File.dirname(__FILE__), 'boot')
require 'has_many_polymorphs'
require 'active_support'
Now for two of the three classes I cannot do a read any more. If i go to localhost:3000/drivers they all list nicely in XML but if i go to localhost:3000/drivers/3 I get an error:
Processing DriversController#show (for 127.0.0.1 at 2009-06-11 20:34:03) [GET]
Parameters: {"id"=>"3"}
[4;36;1mDriver Load (0.0ms)[0m
[0;1mSELECT * FROM "drivers" WHERE ("drivers"."id" = 3) [0m
ActionView::MissingTemplate
(Missing template drivers/show.erb in view path app/views):
app/controllers/drivers_controller.rb:14:in `show'
...etc
This is followed a by another unexpected error:
Processing ApplicationController#show (for 127.0.0.1 at 2009-06-11 21:35:52)[GET]
Parameters: {"id"=>"3"}
NameError (uninitialized constant ApplicationController::AreaAccessDenied):
...etc
What is going on here? Why does the same code work for one class but not the other two? Why is it trying to do a #view on the ApplicationController?
I found that if I create a simple HTML view for each of the three classes these work fine. To each class I add:
format.html # show.html.erb
With this in place, going to localhost:3000/drivers/3 renders out the item in HTML and I get no errors in the log. But if attach .xml to the URL it again fails for two of the classes (with the same error message as before) while one will output XML as expected. Even stranger, on the two failing classes, when adding .js to the URL (to trigger JSON rendering) I get the HTML output instead!
Is it possible this has something to do with the "has many polymorphs" plugin? I have heard of people having routing issues after installing it. Removing "has many polymorphs" and "active support" from environment.rb (and rebooting the sever) seems to make no difference whatsoever. Yet my problems started after it was installed. I've spent a number of hours on this problem now and am starting to get a little desperate, Google turns up virtually no information which makes me suspect I must have missed something elementary. Any enlightenment or hint gratefully received!
JS

If you installed has many polymorphs as a plugin, you have to remove it from vendor/plugins not from environment.rb. Can you please rm -rf that plugin and try again.

Ok. I gave up. Went back to a pre "has many polymorphs" version and appled my changes one by one, running tests after each one to make sure it was still working. About an hour later and I think all the /app/ stuff is the same as when I ran into trouble. "has many polymorphs" is re-installed and required along with "active support" and the classes/models/views are correct (i.e. no HTML render & no views). And guess what; it's all working perfectly! I don't know whether that should make me happy or sad - in any case I'd still really like to know what it was that went wrong here...
JS
P.S. Oh, and if I'm going to continue with RoR I will have to make SVN a priority. It's a total necessity even for such a tiny project since it seems RoR breaks very easily and mysteriously (this is not the first 5+ hour WTF I've had).

You best bet at this time may be to restart your app from scratch. rails is good like that.

Related

How do I correctly handle redirects for bad url's to custom 404 page in ruby on rails?

Users can access others users profiles like this:
site.com/username
My find method in my users controller will find the user by their username.. now if the user doesn't exist I want them re-directed to a custom 404 error page I'll make.
I also want them re-directed for any other non-existant url's they type in.
I've found some solutions on google but wondering if someone can give me an up to date example of doing this in rails 3.2 as theses tutorials I've found are pre rails 3.1.
Kind regards
I tried this solution with Rails 3.2 and it just works.
https://makandracards.com/makandra/12807-custom-error-pages-in-rails-3-2
In your config/application.rb
config.exceptions_app = self.routes
In your config/routes.rb
get '/404', to: 'errors#not_found'
get '/500', to: 'errors#server_error'
Create app/controllers/errors_controller.rb
class ErrorsController < ApplicationController
def not_found
render status: 404
end
def server_error
render status: 500
end
end
Create app/views/errors/not_found.html.haml and app/views/errors/server_error.html.haml (replace haml by erb if you do not use haml)
Restart your server
In production an ActiveRecord::RecordNotFound exception will automatically render your public/404.html file.
you can always render template 404 along with status code when record is not found or invalid URL entered
render :template => 'public/404.html', :status => 404
Here is the blog for "custom dynamic error pages in ruby on rails" will help you
custom dynamic error pages in ruby on rails
isnt it just in /public/404.html ?
UPDATE
Actually im pretty sure it is, that the same exact 404 page that comes up while running my page on nginx in production mode.

Rails, How to render a view/partial in a model

In my model I have:
after_create :push_create
I push_create I need to render a view. I'm trying to do that like so:
def push_event(event_type)
X["XXXXX-#{Rails.env}"].trigger(event_type,
{
:content => render( :partial =>"feeds/feed_item", :locals => { :feed_item => self })
}
)
end
This angers rails as it doesn't like me rendering a view in the model but I need it there.
Error:
NoMethodError (undefined method `render' for #<WallFeed:0x1039be070>):
Suggestions? Should I render it somewhere else somehow? Or how can I render in the model to set content? Thanks
proper solution
Well, "they" are right. You really have to do the rendering in a controller -
but it's fair game to call that controller from a model! Fortunately, AbstractController
in Rails 3 makes it easier than I thought. I wound up making a simple
ActionPusher class, working just like ActionMailer. Perhaps I'll get ambitious and
make this a proper gem someday, but this should serve as a good start for anyone else in my shoes.
I got the most help from this link: http://www.amberbit.com/blog/2011/12/27/render-views-and-partials-outside-controllers-in-rails-3/
in lib/action_pusher.rb
class ActionPusher < AbstractController::Base
include AbstractController::Rendering
include AbstractController::Helpers
include AbstractController::Translation
include AbstractController::AssetPaths
include Rails.application.routes.url_helpers
helper ApplicationHelper
self.view_paths = "app/views"
class Pushable
def initialize(channel, pushtext)
#channel = channel
#pushtext = pushtext
end
def push
Pusher[#channel].trigger('rjs_push', #pushtext )
end
end
end
in app/pushers/users_pusher.rb. I guess the require could go somewhere more global?
require 'action_pusher'
class UsersPusher < ActionPusher
def initialize(user)
#user = user
end
def channel
#user.pusher_key
end
def add_notice(notice = nil)
#notice = notice
Pushable.new channel, render(template: 'users_pusher/add_notice')
end
end
Now in my model, I can just do this:
after_commit :push_add_notice
private
def push_add_notice
UsersPusher.new(user).add_notice(self).push
end
and then you'll want a partial, e.g. app/views/users_pusher/add_notice.js.haml, which could be as simple as:
alert('#{#notice.body}')
I guess you don't really need to do it with Pushable inner class and the .push
call at the end, but I wanted to make it look like ActiveMailer. I also have a
pusher_key method on my user model, to make a channel for each user - but this
is my first day with anything like Pusher, so I can't say for sure if that's the right
strategy. There's more to be fleshed out, but this is enough for me to get started.
Good luck!
(this was my first draft answer, leaving it in because it might help someone)
I've got the general outline of a solution working. Like this, in your model:
after_create :push_new_message
private
def render_anywhere(partial, assigns = {})
view = ActionView::Base.new(ActionController::Base.view_paths, assigns)
view.extend ApplicationHelper
view.render(:partial => partial)
end
def push_new_message
pushstring = render_anywhere('notices/push_new_message', :message_text => self.body)
Pusher[user.pusher_key].trigger!('new_message', pushstring)
end
that is definitely working - the template is rendering, and gets eval()'ed on the client side successfully. I'm planning to clean it up, almost certainly move render_anywhere somewhere more general, and probably try something like this
I can see that pushes will need their own templates, calling the generally available ones, and I may try to collect them all in one place. One nice little problem is that I sometimes use controller_name in my partials, like to light up a menu item, but I'll obviously have to take a different tactic there. I'm guessing I might have to do something to get more helpers available, but I haven't gotten there yet.
Success! Hooray! This should answer your question, and mine - I'll add more detail if it seems appropriate later. Good luck!!!!
original non-answer from an hour ago left for clarity
I don't have an answer, but this timely question deserves more clarification, and I'm hoping to get closer to my answer by helping ask :)
I'm facing the same problem. To explain a little more clearly, Pusher asynchronously sends content to a connected user browser. A typical use case would be a showing the user they have a new message from another user. With Pusher, you can push a message to the receiver's browser, so they get an immediate notification if they are logged in. For a really great demo of what Pusher can do, check out http://wordsquared.com/
You can send any data you like, such as a JSON hash to interpret how you like it, but it would be very convenient to send RJS, just like with any other ajax call and eval() it on the client side. That way, you could (for example) render the template for your menu bar, updating it in its entirety, or just the new message count displayed to the user, using all the same partials to keep it bone-DRY. In principle, you could render the partial from the sender's controller, but that doesn't make much sense either, and there might not even be a request, it could be triggered by a cron job, for example, or some other event, like a stock price change. The sender controller just should not have to know about it - I like to keep my controllers on a starvation diet ;)
It might sound like a violation of MVC, but it's really not - and it really should be solved with something like ActionMailer, but sharing helpers and partials with the rest of the app. I know in my app, I'd like to send a Pusher event at the same time as (or instead of) an ActionMailer call. I want to render an arbitrary partial for user B based on an event from user A.
These links may point the way towards a solution:
http://blog.choonkeat.com/weblog/2006/08/rails-calling-r.html
How to render a Partial from a Model in Rails 2.3.5
http://mattwindsurfs.wordpress.com/2008/06/19/rails-render-in-a-model/
http://davetroy.blogspot.com/2008/02/actsasrenderer-brings-output-to-models.html
https://github.com/asapnet/acts_as_renderer
http://ethilien.net/archives/render-rails-templates-anywhere-even-in-a-model/
The last one looks the most promising, offering up this tantalizing snippet:
def render_anywhere(partial, assigns)
view = ActionView::Base.new(Rails::Configuration.new.view_path, assigns)
ActionView::Base.helper_modules.each { |helper| view.extend helper }
view.extend ApplicationHelper
view.render(:partial => partial)
end
As does this link provided by another poster above.
I'll report back if I get something working
tl;dr: me too!
I just do this:
ApplicationController.new.render_to_string(partial: 'messages/any', locals: { variable: 'value' })
Rails 5 way
In Rails 5 rendering outside a controller became pretty straightforward due to implemented render controller class method:
# render template
ApplicationController.render 'templates/name'
# render action
FooController.render :index
# render file
ApplicationController.render file: 'path'
# render inline
ApplicationController.render inline: 'erb content'
When calling render outside of a controller, one can assign instance variables via assigns option and use any other options available from within a controller:
ApplicationController.render(
assigns: { article: Article.take },
template: 'articles/show',
layout: false
)
Request environment can be tailored either through default options
ApplicationController.render inline: '<%= users_url %>'
# => 'http://default_host.com/users'
ApplicationController.renderer.defaults[:http_host] = 'custom_host.org'
# => "custom_host.org"
ApplicationController.render inline: '<%= users_url %>'
# => 'http://custom_host.org/users'
or explicitly by initializing a new renderer
renderer = ApplicationController.renderer.new(
http_host: 'custom_host.org',
https: true
)
renderer.render inline: '<%= users_url %>'
# => 'https://custom_host.org/users'
Hope that helps.
You can use ActionView directly and render partials to string without having a controller. I find that pattern useful to create models that encapsulate some javascript generation, for instance.
html = ActionView::Base.new(Rails.configuration.paths['app/views']).render(
partial: 'test',
formats: [:html],
handlers: [:erb],
locals: { variable: 'value' }
)
Then, just put your _test.html.erb in you view folder and try it out!
Rails 6.0.0 compatible answer, since I ended up on this page while searching for a solution:
lookup_context = ActionView::LookupContext.new(Rails.configuration.paths["app/views"])
renderer = ActionView::Base.new(lookup_context)
renderer.extend(Rails.application.helpers)
renderer.render \
template: "foo/bar",
formats: [:html],
handlers: [:erb],
locals: { user: User.new }
I'm fairly sure the answers you seek lie within Crafting Rails Applications where Jose Valim goes into great detail about how and why you would want to render views straight from your db
Sorry I can't be of more help yet because I've just started reading it myself tonight.
You might find some help here - it's a blog post about doing this sort of thing, albeit using different methods than yours
the "proper" way to do this is to push an object in serialized form(json), and then have the view deal with it once the event is received. Perhaps you want to use Handlebars to render the object.
Edit: I originally wrote about how, despite my answer, I was going to follow your example. But I just realized there is a HUGE gotcha with your approach when it comes to push notifications.
In your problem, you are doing push notifications to one user. For me, I was broadcasting out to a set of users. So I was going to render html with a presumption of a "current_user" and all that comes with it(eg logic, permissions, etc). This is NO BUENO as each push notification will be received by a different "current user".
Therefore, really, you need to just send back the data, and let each individual view handle it.
You should call all render methods from a controller. So, in this case, you can notify the controller that the object has been created and the controller can then render the view. Also, since you can render only once, I think you can wait for all your server side operations to complete before invoking the render.
The render methods are defined on the ActiveController class and its progeny. Inherently you do not have access to it on the model, nor is it a class method so you can't use it without an instance of the controller.
I've never tried to instantiate a controller for the express purpose of simply stringifying a partial, but if you can get your hands on a controller, render_to_string seems to be the way to go.
I will chime in by saying that if you're going down this path you're taking RoR "off the Rails". This is a violation of MVC and fundamentally poor program design.This doesn't mean I think you're a bad person :P Sometimes life drives us off the rails, so to speak.
I can't speak to the details that have driven you to do this, but I'd strongly suggest you rethink your approach.
I have created a gist for this.
I needed something similar, where the models don't necessarily (or in my case, ever) get updated via a controller, so the logic can't sit there.
Created a server-push based controller:
https://gist.github.com/4707055

Rails 3 rendering template missing without file ending

I have an action in my controller as below:
def show
#post = Post.find_by_setitle(params[:setitle])
if !#post
render 'error_pages/404'
return
end
respond_to do |format|
format.html
end
end
If the render error_pages/404 I get a template missing. Switching it to render error_pages/404.haml.html works fine.
Why is this?
N.B. There is no actual error_pages controller or model. Just a convenient place to keep them.
Edit: I'm using mongoid and hence don't have access to ActiveRecord. Controller base can't be looking for a particular ActiveRecord exception?
From the documentation
The render method can also use a view that’s entirely outside of your application (perhaps you’re sharing views between two Rails applications):
Rails determines that this is a file render because of the leading slash character. To be explicit, you can use the :file option (which was required on Rails 2.2 and earlier):
You need either to pass the :file option, or to start the location string with a slash. Alternatively, you could use the Rails functionality to rescue from errors, and recover from ActiveRecord::RecordNotFound with a 404. See this post for details.
You should probably use render :template => 'error_pages/404'.
I think Rails is looking for a partial called _404.
Try it out 1:
render 'error_pages/404' (and name the file _404.html.erb)
Try it out 2:
render :template => 'error_pages/404' (and name the file 404.html.erb i.e. no leading underscore)

Some issue with Ajax on Rails

I'm using Ruby on Rails 2.3.8. I've got a vote link that when you click it executes the following action:
def vote
render :update do |page|
page.select(".divbrian").each do |d|
page.replace_html d, "YA SEEE"
end
page.select('.d_voting_links_' + params[:post_id].to_s).each do |d|
page.replace_html d, :partial => 'post_votes/voted'
end
page.select('.d_vote_count_' + params[:post_id].to_s).each do |d|
page.replace_html d, Post.find(params[:post_id]).get_vote_count
end
end
end
In the HTML, there are as much divs of class "d_voting_links_" as posts displayed in the html page.
I don't know why, after I've installed Paperclip, Rails TinyMCE, and Hpricot plugins (all of them on the latest version), these ajax requests stopped working (really crazy).
I haven't changed anything of my code, I've even checked on my previous versions in which it works and nothing had changed, but those plugins.
I've also tried to created a simple div and calling it from the same action but it didn't recognize it (it does if I delete the "." from the request, but that will search for IDs, and I need to search for class).
Do you know about any known issue between those plugins and this ajax syntax? Or maybe...do you have a clue about what the problem could be?
I don't know why that block of code stopped working...but I've now figured out that without the page.select block, it will work anyway and replace all of the divs with those class names.

How to render a Partial from a Model in Rails 2.3.5

I have a Rails 2.3.5 application and Im trying to render several Partials from within a Model (i know, i know -- im not supposed to). The reason im doing this is im integrating a Comet server (APE) into my Rails app and need to push updates out based on the Model's events (ex. after_create).
I have tried doing this:
ActionView::Base.new(Rails::Configuration.new.view_path).render(:partial => "pages/show", :locals => {:page => self})
Which allows me to render simple partials that don't user helpers, however if I try to user a link_to in my partial, i receive an error stating:
undefined method `url_for' for nil:NilClass
I've made sure that the object being passed into the "project_path(project)" is not nil. I've also tried including:
include ActionView::Helpers::UrlHelper
include ActionController::UrlWriter
in the Module that contains the method that makes the above "render" call.
Does anyone know how to work around this?
Thanks
We use the render_anywhere gem and have been happy with it.
From the README:
require 'render_anywhere'
class AnyClass
include RenderAnwhere
def build_html
html = render :template => 'normal/template/reference',
:layout => 'application'
html
end
end
Including these two modules should be enough. Maybe you forgot to set default_url_options[:host]? Without it you can use _path helpers, but not _url ones.
Include these modules and check out if it works in irb, maybe it will lead you to right solution.

Resources