For example, say you want to do some non-Web-based report that you run from the command line. Or a database maintenance script that you want to run regularly (so it wouldn't make sense to put it in a migration, since those are only expected to be run once). Or anything else you'd call from cron.
#!/bin/env ruby
$LOAD_PATH << "../"
require "config/environment"
require "app/models/book.rb"
result = Book.connection.select_all("select * from books")
Or: require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
One could probably do something like this too (haven't tried it yet), which would automatically load the environment before executing the code that you pass to runner:
./script/runner "require 'send_mailing'"
$ ./script/runner -e production 'puts ENV["RAILS_ENV"]' production
Notice that this doesn't work:
$ ./script/runner "puts MainController.new"
/usr/lib/ruby/gems/1.8/gems/rails-1.1.6/lib/commands/runner.rb:27: /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:123:in `const_missing': uninitialized constant ApplicationController (NameError)
from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:131:in `const_missing'
from script/../config/../app/controllers/main_controller.rb:3
from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:140:in `load'
from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:140:in `load'
from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:56:in `require_or_load'
from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:30:in `depend_on'
from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:85:in `require_dependency'
from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:98:in `const_missing'
from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:131:in `const_missing'
from (eval):1
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `eval'
from /usr/lib/ruby/gems/1.8/gems/rails-1.1.6/lib/commands/runner.rb:27
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require__'
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require'
from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:147:in `require'
from ./script/runner:3
But this does work:
]$ ./script/console
Loading development environment.
Welcome!
irb -> MainController.new
=> #<MainController:0xb7aefaa8>
So after examining the source of script/console and commands/console, I think I figured out what the difference was that allowed console to have access to it but not runner:
12 libs = " -r irb/completion"
13 libs << " -r #{RAILS_ROOT}/config/environment"
14 libs << " -r console_app"
15 libs << " -r console_sandbox" if options[:sandbox]
16 libs << " -r console_with_helpers"
This seems to work:
$ ./script/runner "require 'console_with_helpers'; puts MainController.new" #<MainController:0xb78ee6dc>
Ugly, but it works.
I don't know. I've tried to get it to work and failed.
http://lists.rubyonrails.org/pipermail/rails/2006-March/028892.html [Rails] Re: runner: url_for with mailers impossible
http://wrath.rubyonrails.org/pipermail/rails-core/2006-June/001652.html [Rails-core] The old deal with url_for in Mailer
This
puts MainController.new.url_for(:action => 'a')
causes this:
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:490:in `url_for': You have a nil object when you didn't expect it! (NoMethodError) The error occured while evaluating nil.rewrite from ./script/whatever.rb:5
This:
http://wiki.rubyonrails.org/rails/pages/HowtoUseUrlHelpersWithActionMailer/ HowtoUseUrlHelpersWithActionMailer in Ruby on Rails
seems to suggest that maybe this would work:
include ActionView::Helpers::UrlHelper puts url_for(:action => 'a')
but again we get:
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:490:in `url_for': You have a nil object when you didn't expect it! (NoMethodError)
The error occured while evaluating nil.rewrite from /usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/url_helper.rb:27:in `send'
from /usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/url_helper.rb:27:in `url_for'
from ./script/whatever.rb:7
Do I have to set it to a member variable, and then add <%= my_debug_output %> to the template??
Do I have to use a logger instead?
Can't I just say puts my_debug_output? Or render :text => my_debug_output? And have it added to the HTML that will be rendered?
That's one thing I miss about PHP. In PHP, you can put a echo/print statements everywhere to help you debug your code.
My solution: Usually I just use
raise whatever_object.inspect
Sometimes I'll use a global, something like this:
$output = '' $output += 'some debug output'
And outputting <%= $output %> in my view. But really I should just be using the logger at that point.
both render the view (.rhtml file).
Only redirect_to causes the action in the controller to be called. So if you are doing render and any set up needs to be done, you need to do it in the action method that is calling render or you need to explicitly call the action before you render the template for that action (confusing, I know!).
render keeps the local variables (such as error messages and the current, possibly invalid state of the objects being edited); redirect loses them
both of them keep and show the contents of flash
[need real example]
Yes! Just put something like the following in your ActionMailer::Base subclass:
helper :application, :other
It's sort of like an around_filter, only cooler! (available as a plugin, I think)
after_filter happens after the view, not after the controller (http://woss.name/2006/05/07/notes-from-a-rails-course/0)
Normally the flash message will be available to the next action, whatever that is...
Maybe you're setting flash and then rendering and wanting your message to only appear for the current action.
Solution: Try setting flash.now[:error] = 'whatever' instead of flash[:error].
Reference: http://api.rubyonrails.org/classes/ActionController/Flash/FlashHash.html#M000142
Rails Notification System (http://blog.andreasaderhold.com/2006/07/rails-notifications) (July 13, 2006).
Here’s a nice trick for building a cool notification system that uses the Rails flash array and can be used with Ajax and with normal actions. I’ve used this on two projects and it works just fine (not much that can go wrong anyways). First, put the following in your layout template:
<% flash.each do |key,value| -%> <h4 id="flash" class='alert <%= key %>'> <%= value %> </h4> <% end -%>[Incorrect! This will cause there to be more than one element with id="flash", which is invalid. I would make "flash" a class rather than an id.
<h4 class='flash <%= key %>'>]This gives you a dynamic flash thing, so you can use different type of notifications/alerts (i.e. falsh[:error], flash[:critical], etc.)
The styles for the default types error and notice could look something like this [...]:
h4.alert { font-size: 0.8em; font-weight:bold; margin:0; padding:5px; } h4.error { color:#fff; background:#c00; } h4.notice { color:#060; background:#e2f9e3; }Ok, so far so good. This is quite standard. Because typing flash[:blah] = "Something" is boring and after all we want to send a message to the notification system and don’t want to care about arrays, it’s just logical we want to do something like
notify :error, "Something went wrong". So let’s define a helper method in our application.rb:def notify(type, message) flash[type] = message logger.error("ERROR: #{message}") if type == :error endNote, this also creates a log entry in case the type is :error. Now that’s it. You have a nice notification system, the CSS class names are named by the notification types and you have easy handler method to fire a note. Oh let it DRY baby. Bonus: Fading messages
It’s optional, but I like it. I don’t want the messages to stand in the layout all the time. It was a notification and that’s it. You would not want to have the credits of a movie scrolling on top of the screen all the time, would you? I don’t, so let’s just fade away the message by adding the following snippet to our application.js file:
/* fade flashes automatically */ Event.observe(window, 'load', function() { $A(document.getElementsByClassName('alert')).each(function(o) { o.opacity = 100.0 Effect.Fade(o, {duration: 8.0}) }); });This requires script.aculo.us, so don’t forget to include it. It softly fades out all flash messages over 8 seconds. You can extend/reduce the duration of course. Wait, what about those Ajax actions?
Helpers to rescue. Since we have the RJS stuff and helper methods are available to the page object, this is a no-brainer. Let’s define the notify function in application_helper.rb:
def notify(type, message) type = type.to_s # symbol to string page.replace 'flash', "<h4 id='flash' class='alert #{type}'>#{message}</h4>" page.visual_effect :fade, 'flash', :duration => 8.0 endOk, this makes a notify method avail so we can send the page object a message:
render :update do |page| # do your stuff here page.notify :notice, "Successfully closed all windows" endThe last step is to extend your main flash rendering code to something like this:
<% if flash.empty? %> <h4 id="flash" class="alert" style="display:none"></h4> <% else %> <% flash.each do |key,value| -%> <h4 id="flash" class='alert <%= key %>'> <%= value %> </h4> <% end -%> <% end %>This ensures we always have a element with the ID “flash” rendered, so the notify call can update it.
It bothers me how the prevailing convention is to set the flash value rather than appending to it: flash[:whatever] = 'My message'.
What if you have, say, some before_filter in your ApplicationController that may want to flash a message in certain cases, and your specific controller action also wants to flash a message? Whichever one sets the flash hash first "wins", silently overwriting the other message and preventing it from ever getting seen... bad!!
One possible solution would be to always append to the string:
flash[:notice] << 'My message'
But then your messages will run together. So you could try to remember (I say "try", because you know you'll forget some of the time) to put each message in an HTML paragraph:
flash[:notice] << '<p>My message</p>'
Instead of having each entry in the hash be a string, each entry should be an array of strings. Or something like that. So you'd have a list (array) of 0 or more error messages, 0 or more notices, etc.
flash[:notice] << 'My message'
You would have to change how you rendered it, though. To something like this:
<% flash.each do |flash_type, messages| -%>
<h4 class='flash <%= flash_type %>'>
<% messages.each do |message| -%>
<p>
<%= message %>
</p>
<% end -%>
</h4>
<% end -%>
No, I think the application developers should be able to use whichever ones make sense to the application. I mean, they should be consistent themselves, but I'm not sure we need to adopt a Rails-community-wide standard here...
Here's one person's proposal:
standardizing rails flash messages [ruby [rails] [flash]] (http://snippets.dzone.com/posts/show/1348).
:noticefor positive feedback (action successful, etc):messagefor neutral feedback (reminders, etc):warningfor negative feedback (action unsuccessful, error encountered, etc)
I don't agree with it though. I would suggest using one of these conventions:
:notice for positive or neutral feedback (action successful, etc):error for negative feedback (action unsuccessful, error encountered, etc.):positive for positive feedback (action successful, etc):neutral for neutral feedback (reminders, etc):warning for somewhat negative feedback (warnings):error for very negative feedback (more fatal-type erorrs)David Reynolds said:
http://manuals.rubyonrails.com/read/chapter/57 Rails Cookbook: file upload forms
http://www.kanthak.net/opensource/file_column/ Sebastian Kanthak - FileColumn - easy handling of file uploads in Rails
http://www.railsdiary.com/diary/validation_securing_form_input The Rails Diary | Validation and Securing Input
@company.update_services(params[:services]) def update_services (services_hash) if services_hash services.clear services_hash.each { |service_id, value| services << Service.find(service_id.to_i) } end
You can just set the collection directly using the 'things_id =' or (?) 'things =' accessor/settor methods!
http://wiki.rubyonrails.com/rails/pages/HowtoUseFormOptionHelpers/:
Assigning to has_many or has_and_belongs_to_many collections If you want to have your multiple choice select assign to a has_many or has_and_belongs_to_many (habtm) collection on your object, you should specify the collection using the <collection name>_ids name. For example, if we have an AddressGroup object with a collection of Address objects called addresses, we would not use this: <select name="addressgroup[addresses][]" multiple="multiple"> but rather this: <select name="addressgroup[address_ids][]" multiple="multiple"> With either select statement the returned value is an array of Address object ids, not the actual array objects, which will fail if we tried to assign to addressgroup.addresses. If you try to do that you will get an error message like “Expected Address and got String”. Instead, we use the implicit address_ids= property (automatically added to an object when it has a has_many or habtm collection), which assigns to the collection based on a collection of IDs.
[Caveat (category)][Setting has_many association (category)]
For example, you have:
Profile Model:
habtm :hobbies
View:
<%= select("profile", "hobbies", @hobbies.options_for_select, { :include_blank => true }) %>
Controller:
@profile = Profile.new(params[:profile])
When you submit the form, it says "Hobby expected, got String".
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/association_proxy.rb:134:in `raise_on_type_mismatch' /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/association_collection.rb:118:in `replace' /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/association_collection.rb:118:in `replace' /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:895:in `hobbies='
Solution:
In this case, I made it extra difficult, because I'm using a single-select field (typically used for has_many associations, not habtm) to a habtm (usually a multi-select would be used).
If I were using a multi-select, this would be all I need to change:
View:
<%= multi_select("profile", "hobby_ids", @hobbies.options_for_select, { :include_blank => true }) %>
In my case, however, I need to convert my single string value submitted in params[:profile][:hobby_ids] (for example, "3") into an array of strings (["3"]). So I did this (which could easily be thrown into the model logic):
for key, value in params[:profile]
if key.match /_ids/ and !value.is_a?(Array)
params[:profile][key] = [value]
end
end
# "Model" object
class Address
def line_1
"123 Fake St."
end
def line_2
"Apt. 42"
end
end
@address = Address.new
# Helpers
text_field("address", "line_1")
text_field("address", "line_2")
# Produce essentially this HTML output:
<input type="text" name="address[line_1]" />
<input type="text" name="address[line_2]" />
# Produces these params:
"address"=>{"line_1"=>"123 Fake St.", "line_2"=>"Apt. 42"}}
params[:address][:line_1] = "123 Fake St."
params[:address][:line_2] = "Apt. 42"
<input type="text" name="address[][line_1]" />
<input type="text" name="address[][line_2]" />
# Not very useful
"address"=>{""=>{"line_1"=>"234", "line_2"=>"234"}
<input type="text" name="address[lines][]" />
<input type="text" name="address[lines][]" />
"address"=>{"lines"=>["123 Fake St.", "Apt. 42"]
params[:address][:lines[0] = "123 Fake St."
params[:address][:lines[1] = "Apt. 42"
http://i.nfectio.us/articles/2006/02/22/handling-invalid-dates-with-activerecord-date-helpers :
Plugin:
http://wiki.rubyonrails.org/rails/pages/Validates+Multiparameter+Assignments :