I am new to Ruby and Rails. I am playing with Rack, trying to get a basic understanding of this peice of the Rails puzzle, following along with Rob Conery in his Tekpub/Rails 3 tutorial vid.
Unfortunately, the version of Rack used in the vid has become long in the tooth, and I think something has changed in between the video release and now (as have some things in Ruby between 1.8.x and 1.9.x). Even more unfortunately, I don't yet know enough about Ruby or Rack to know how to figure out what I need to do differently. The version of Rack used in the video is 1.1. The version on my machine is 1.4.5.
Silly example code:
class EnvironmentOutput
def intialize(app)
#app = app
end
def call(env)
out = ""
unless(#app.nil?)
response = #app.call(env)[1]
out+=response
end
env.keys.each {|key| out+="<li>#{key}=#{env[key]}"}
["200", {"Content-Type" => "text/html"}, [out]]
end
end
class MyApp
def call(env)
["200", {"Content-Type" => "text/html"}, ["<h1>Hello There</h1>"]]
end
end
# My understanding is that this should work:
use EnvironmentOutput
run MyApp.new
When I run this, I get the following:
ArgumentError: wrong number of arguments(1 for 0)
This is where the first in a series of errors occurs (line 82 in the rack Builder class):
def use(middleware, *args, &block)
if #map
mapping, #map = #map, nil
#use << proc { |app| generate_map app, mapping }
end
# error occurs HERE:
#use << proc { |app| middleware.new(app, *args, &block) }
end
Obviously, I am passing something incorrectly. Sadly, I don't yet know enough to figure out what it is I am doing wrong. I have searched on Google and here on SO, but I suspect I also don't have quite a strong enough grasp on the Ruby/Rails/Rack relationship to know what to ask and get a reasonably helpful result (or, if I AM, then I don't yet recognize it).
Does anyone know what I am doing wrong here?
UPDATE: Thanks to the selected answer, I realize it was a typo. Next issue is an array-to-string conversion problem in the same code, but will post as new question.
It’s just a typo:
def intialize(app)
should be
def initialize(app)
(you’ve missed an i).
Since you don’t provide an initialize method, Ruby tries to use the default, argument-less one, but since Rack passes an argument (the app) you get the ArgumentError.
Related
I'm new to Ruby and Rails, coming from Java and Playframework.
I'm following a tutorial http://www.sitepoint.com/building-your-first-rails-application-views-and-controllers/
I am getting an error on the line #shortened_url = Url.new
The error is :
NameError in UrlsController#new
uninitialized constant UrlsController::Url
I feel this is something like a ClassNotFoundError in Java ?? not sure ... does anyone know what I have to do. Is it basically a matter of using include or require, with the correct reference.
Apologies for the total newb question but I find it much easier to learn by doing and making mistakes, it sticks - and I reckon others will benefit too.
class UrlsController < ApplicationController
def new
#shortened_url = Url.new
end
def create
#shortened_url = Url.new(params[:url])
if #shortened_url.save
flash[:shortened_id] = #shortened_url.id
redirect_to new_url_url
else
render :action => "new"
end
end
def show
#shortened_url = Url.find(params[:id])
redirect_to #shortened_url.url
end
end
May or may not be the issue you're having, but Url is a reserved word in Rails.
Source: http://bparanj.blogspot.co.uk/2011/07/reserved-words-in-rails.html
Alternatively, it's weird that calling Url.new is calling new on your UrlsController instead of the Url model. This backs up my idea about using reserved words, it can often cause strange behaviour.
Edit: Oh and it looks like from another comment that you don't have a Url model. You'll need one of those before you can called .new on it. That said, don't create a model called Url, the reserved word thing will probably come back to bite you.
I've made a custom exception handler, and it works greatly for the most part. Here's the gist of it-
app/errors/duck.rb
module Duck
def Duck::ReportError(u,c,m,p)
User.send_error_email(u,c,m,p)
User.msg(m)
end
end
app/mailers/notifications.rb
def err_report(current_user,err_script,err_msg,err_scrub)
#current_user = current_user
#err_script = err_script
#err_msg = err_msg
#err_scrub = err_scrub
mail(:to => sr_dev, :cc => jr_dev, :subject => "Error Found")
end
And this is where my problem comes in- the code above is as it is written currently. There were some minor changes to eliminate repetition. I would previously do my calls as follows- any error was handled like this example where this would be placed within all the actions in users_controller.rb...
rescue Exception => msg
Duck.ReportError(current_user,"users_controller.rb",msg,"Dumping params: #{params.inspect}")
Any error must result in a duck call- thus the previous repetition.. which didn't accomplish this. It also completely obliterates DRY, so I re-coded the duck.rb file and found that by placing it in the application controller, anything that inherits from ApplicationController could raise the duck like this-
rescue_from Exception, :with => :any_error
protected
def any_error(msg)
Duck.ReportError(current_user,"application_controller.rb",msg,"Dumping params: #{params.inspect}")
render "home/duck.erb"
end
The rest of the code is trivial to the issue at hand, which is this:
I need to show the dev team which file the error originated from. When I first made the Duck, it was called from each action/view/controller directly, as previously shown, which was a lot of repetition, but had the benefit that I could tell it specifically which script generated the error. Now I've somewhat abstracted the error handler, which is great- but every time an error comes in from, for example, reports_controller.rb, it reports the error as coming from application_controller.rb. How can I fix this? How do I get the actual source of the error?
System:
$ rails -v
Rails 3.0.20
$ ruby -v
ruby 1.8.7 (2012-10-12 patchlevel 371) [x86_64-linux]
I'm using the rails-footnotes gem in my Rails 3.2 applications, but I can't seem to get the footnotes to register the existence of any partials: it always shows a zero partial count.
To be able to know how many and what partials are being displayed on a view easily like this is, I think, immensely useful, so I would really like to get this working (the rest works great), but I'm not sure what the problem is and am hoping someone else has had the same problem and was able to resolve it. Is there a setting I've potentially missed?
I don't think it's relevant, but I'm using OSX 10.6.8 and had some issues getting the gem to work with Sublime Text 2 properly, but they did get resolved (details in this StackOverflow answer).
Update:
It seems that the issue only exists for haml templates, as I am getting expected output for erb templates. It would seem that only erb templates are counted/recognized...?
Update 2:
#DonHopkins' answer below got all my Haml templates to register with Rails Footnotes. I put it in my config file as follows:
config/initializers/rails_footnotes.rb
if defined?(Footnotes) && Rails.env.development?
Footnotes.run! # first of all
Footnotes::Notes::LogNote::LoggingExtensions.module_eval do
def add(*args, &block)
super
logged_message = args[2] + "\n"
Footnotes::Notes::LogNote.log(logged_message)
logged_message
end
end
# ... other init code
Footnotes::Filter.prefix = 'subl://open?url=file://%s&line=%d&column=%d'
end
I had a similar problem, although I am using erb templates, not haml. I fixed it with a monkey patch to rails-footnotes.
Looking at the rails-footnotes code (version 3.7.9), it looked to me like the problem is in this method:
module Footnotes
module Notes
class LogNote < AbstractNote
...
module LoggingExtensions
def add(*args, &block)
logged_message = super
Footnotes::Notes::LogNote.log(logged_message)
logged_message
end
end
...
end
end
end
The add method is assuming that super returns the message that is being logged, but in my testing super was returning a boolean value. To solve the problem, I created a file called footnotes_patch.rb with the following:
Footnotes::Notes::LogNote::LoggingExtensions.module_eval do
def add(*args, &block)
super
logged_message = args[2] + "\n"
Footnotes::Notes::LogNote.log(logged_message)
logged_message
end
end
If you want to try the solution, put that file in config/initializers, then restart your application.
Rails ActiveResource is awesome ... except for one thing: as far as I can tell, there is no way to see what URL it is using behind the scenes. For instance, let's say I have an ActiveResource called Issue, for a webservice at myIssues.com/issues.xml. If I do:
Issue.find(:all, :params => {:page => 2})
I would expect that ActiveResource would make a call to:
myIssues.com/issues.xml?page=2
... but I don't actually know that. For all I know, ActiveResource could have decided it doesn't like the word "page", so it's actually using:
myIssues.com/issues.xml?mod_page=2
This makes debugging difficult. Right now I've got a situation where, if I go to the URL I think ActiveResource is using, it works just fine. However, when I actually use ActiveResource, it doesn't work. Seeing the URL it's GETing would be immensely helpful in this, so ...
Does anyone know a way to log (or otherwise output; if there's some resource.url method that would work great too) the URL(s) that ActiveResource uses to do its thing?
If you add the following line to your environment.rb file, it will at least log the requests so you know that URLs ActiveResource is hitting:
ActiveResource::Base.logger = ActiveRecord::Base.logger
I'm still searching for a better solution that shows me the response and the data posted to update calls, but at least this is a step in the right direction. I'm really not sure why ActiveResource has a separate logger to start with, but that's another matter.
I just ran into this same exact issue, and came across this post as I was looking for answers. What I did find, that proved useful, is the collection_path method on ActiveResource::Base. So for example, let's say you have the following resource:
class UserPost < ActiveResource::Base
self.site = "http://someApp.com/user/:user_id"
self.element_name = "post"
If you go to the rails console, here are some examples of the output:
>> UserPost.collection_path
"/user//post"
>> UserPost.collection_path(:user_id => 5)
"/user/5/post
This should provide you with exactly what you need to determine how ActiveResource is translating your request into a URL.
To get detail login for ActiveResource have to patch the request method inside the gem(method.
place bellow files inside config/initializers you will get http method, path, request body, request hedaers
response body and header is already there if you need. doc
config/initializers/activeresource_patch.rb
module ActiveResource
class Connection
private
def request(method, path, *arguments)
result = ActiveSupport::Notifications.instrument("request.active_resource") do |payload|
payload[:method] = method
payload[:request_uri] = "#{site.scheme}://#{site.host}:#{site.port}#{path}"
payload[:request_path] = path
payload[:request_body] = arguments[0]
payload[:request_headers] = arguments[1]
payload[:result] = http.send(method, path, *arguments)
end
handle_response(result)
rescue Timeout::Error => e
raise TimeoutError.new(e.message)
rescue OpenSSL::SSL::SSLError => e
raise SSLError.new(e.message)
end
end
end
config/initializers/activeresource_logger.rb
Rails.application.configure do
def activeresource_logger
#activeresource_logger ||= Logger.new("#{Rails.root}/log/activeresource_logger.log")
end
ActiveSupport::Notifications.subscribe('request.active_resource') do |name, start, finish, id, payload|
if Rails.env.development?
activeresource_logger.info("====================== #{start} : #{payload[:method].upcase} ======================")
activeresource_logger.info("PATH: #{payload[:request_path]}")
activeresource_logger.info("BODY: #{payload[:request_body]}")
activeresource_logger.info("HEADERS: #{payload[:request_headers]}")
# activeresource_logger.info("STATUS_CODE: #{payload[:result].code}")
# activeresource_logger.info("RESPONSE_BODY: #{payload[:result].body}")
end
end
end
I have a Rack application that looks like this:
class Foo
def initialize(app)
#app = app
end
def call(env)
env["hello"] = "world"
#app.call(env)
end
end
After hooking my Rack application into Rails, how do I get access to env["hello"] from within Rails?
Update: Thanks to Gaius for the answer. Rack and Rails let you store things for the duration of the request, or the duration of the session:
# in middleware
def call(env)
Rack::Request.new(env)["foo"] = "bar" # sticks around for one request
env["rack.session"] ||= {}
env["rack.session"]["hello"] = "world" # sticks around for duration of session
end
# in Rails
def index
if params["foo"] == "bar"
...
end
if session["hello"] == "world"
...
end
end
I'm pretty sure you can use the Rack::Request object for passing request-scope variables:
# middleware:
def call(env)
request = Rack::Request.new(env) # no matter how many times you do 'new' you always get the same object
request[:foo] = 'bar'
#app.call(env)
end
# Controller:
def index
if params[:foo] == 'bar'
...
end
end
Alternatively, you can get at that "env" object directly:
# middleware:
def call(env)
env['foo'] = 'bar'
#app.call(env)
end
# controller:
def index
if request.env['foo'] == 'bar'
...
end
end
Short answer: Use request.env or env inside a controller.
Long answer:
According to the Rails Guide on Rails controllers, ActionController provides a request method that you can use to access information about the current HTTP request your controller is responding to.
Upon further inspection of the docs for ActionController::Base#request, we see that it "Returns an ActionDispatch::Request instance that represents the current request."
If we look at the docs for ActionDispatch::Request, we see that it inherits from Rack::Request. Aha! Here we go.
Now, in case you're not familiar with the docs for Rack::Request, it's basically a wrapper around the Rack environment. So for most cases, you should just be able to use it as-is. If you really do want the raw environment hash though, you can get it with Rack::Request#env. So within the Rails controller, that would just be request.env.
Digging deeper:
After further examining the instance methods of ActionController::Base, I noticed there's not a whole lot there to look at. In particular, I noticed the params and session variables seem to be missing. So, I moved up one level to ActionController::Metal, which ActionController::Base inherits from.
In ActionController::Metal, I discovered a method env which had no documentation as to what it did - but I could guess. Turns out I was right. That variable was being assigned to request.env.
ActionController::Metal also contained the params method, which, according to the source, was set to request.parameters by default. As it turns out, request.parameters isn't from Rack::Request, but ActionDispatch::Http::Parameters, which is included by ActionDispatch::Request. This method is very similar to the Rack::Request#params method, except that altering it modifies a Rails-specific Rack environment variable (and therefore changes will remain persistent across instances of ActionDispatch::Request).
However, I still couldn't seem to find the session method. Turns out, it's not in the documentation at all. After searching the source code for ActionController::Metal, I finally found it on this line. That's right, it's just a shortcut for request.session.
To summarize:
In the controller...
Use request.env or env to get at the raw environment object
Use params to read Rack query strings and post data from the rack input stream. (E.g. Rack::Request#params)
Use session to access the value of rack.session in the rack environment
In the middleware...
Access properties of the environment the usual way through the environment hash
Access the Rails session through the rack.session property on the environment hash
Read params through Rack::Request#params
Update params through Rack::Request#update_param and Rack::Request#delete_param (as stated in the docs for Rack::Request#params)
Update params in a Rails specific way using ActionDispatch::Http::Parameters#params through ActionDispatch::Request