Installing rdoc 2.4.3 ruby gem from source
Have you ever messed up with your gems? Well, I did!
Having a conflict with rdoc versions I felt the need to remove rdoc 2.4.3
As personal advise, if you read this article before you do so, DO NOT DO IT ;-)
I'm on snow leopard and once rdoc 2.4.3 uninstalled all rubygems got blocked. Not even possible to install local gems with
gem install -l path/to/my/local/gem.gem
Installing rdoc 2.4.3 from source
What to do next, `gem` command broken, ... I went for the source installation downloading the tarball rdoc-2.4.3.tgz
Install the gem source
First things first, we need to untar the rdoc source in the appropriate folder and give it the right permissions
mv rdoc-2.4.3.tgz /Library/Ruby/Gems/1.8/gems/
cd /Library/Ruby/Gems/1.8/gems/
tar xvzf rdoc-2.4.3.tgz
sudo chown -R root:admin rdoc-2.4.3
Install the gem file as cache
Download the gem file, and save it as cachedrdoc-2.4.3.gem
mv rdoc-2.4.3.gem /Library/Ruby/Gems/1.8/cache/
sudo chown root:admin /Library/Ruby/Gems/1.8/cache/rdoc-2.4.3.gem
Install the gemspec file
Here came a problem, where to find the gemspec?
I couldn't find so asked a friend, Sébastien Gruhier from Xilinus, thanks to him you can download rdoc-2.4.3.gemspec
mv rdoc-2.4.3.gemspec /Library/Ruby/Gems/1.8/specifications/
sudo chown root:admin /Library/Ruby/Gems/1.8/specifications/rdoc-2.4.3.gemspec
From this point, I was able to run `gem list` again, and all went back to normal, conflict included ;-)
Maps, Geolocalization and Optimization with Maptimize
Playing with maps is great fun and here is walk through from basics to advanced use of geolocalization and how to optimize your maps with Maptimize
During this tutorial you will :
- start geo localizing your addresses with server side technologies and caching system
- add front side tools (in Javascript) to keep things lighter and nicer for the end user
- keep both side geo localization working nicely together and in an unobtrusive way
- map your localized object while keeping optimized, using Maptimize
- show details when clicking on markers and clusters
- bonus, use Maptimize makers when creating / editing entries
UPDATE: Thanks to Sébastien Gruhier from Xilinus I’ve made couple of change to improve various codes and this article in general.
UPDATE 2 – 2009-05-04: Sébastien Gruhier from Xilinus has been rocking again and helped me out improving few aspects.
UPDATE 3 – 2009-05-04: Improve maptimize configuration and task to be more flexible and portable
Feeling in hurry ?
I’ve made a basic demo running on Rails 2.3.2 to cover this tutorial : Maptimize – Demo 1 so feel free to check it out directly. Make sure to check the README.textile to get it working.
With more time, let’s get a step by step walk through
For this integration I’ll assume that you have a Rails application up-and-running (for me using Rails 2.3.2) with a model collecting address details, in my case, “business”
Geolocalize your addresses
You first gonna need to add the lat and lng float attributes in your database but also a geolocalized_address string attribute. This attribute will be used to cache the address and prevent unnecessary updates.
From the demo :
class AddGeolocalizedSupportToBusinesses < ActiveRecord::Migration def self.up change_table(:businesses) do |t| t.float :lat, :lng t.string :geolocalized_address end end def self.down change_table(:businesses) do |t| t.remove :lat, :lng, :geolocalized_address end end end
Don’t forget to run the migration rake db:migrate
1. Geolocalize on server side with GeoKit
Make sure to install GeoKit gem and GeoKit rails plugin as well as configuring your KEYS in “config/initializers/geokit_config.rb“
GeoKit propose you a really simple way to automatically geolocalize your address :
class Business < ActiveRecord::Base acts_as_mappable :auto_geocode=>true end
In our case, we will do something a bit more advanced by caching the geolocalized address to prevent calling the Geolocalization Services when we don’t need to.
Here is the model that I would like to geolocalize :
class Business < ActiveRecord::Base acts_as_mappable before_validation :geocode_address private def geocode_address return if self.address.nil? || (self.geolocalized_address == self.address && !self.lat.nil? && !self.lng.nil?) geo=Geokit::Geocoders::MultiGeocoder.geocode(address) errors.add(:address, "Could not Geocode address") if !geo.success self.lat, self.lng, self.geolocalized_address = geo.lat, geo.lng, self.address if geo.success end end
2. Geolocalize on front-end side
As Google will recommend and warn when signing up for the API key their is a geocode request limitation per day based on your ip. Using front side geocoding will prevent you from reaching this limits as the geolocalization request will be done from your customers IP address.
Client side geolocalization will also save server load and allow your customers to adjust the position when needed.
For this purpose, I’m using AddressChooser from Maptimize , and as dealing with a single address field I’m gonna add the prototype / scriptaculous autocomplete example
NOTICE : AddressChooser is Javascript framework-agnostic and Mapping system independent, so if your needs differs from mine go check AddressChooser from Maptimize to get the full details.
Start by including the required Javascripts files :
- Prototype JS
- Scriptaculous Effects and Controls
- AddressChooser
- Your own JavaScripts
I actually start to use more and more the Google hosted ones to prevent end users from loading this libraries again and again so here is how I deal with it:
<script type="text/javascript" src="http://www.google.com/jsapi?key=<%= Geokit::Geocoders::google %>"></script> <%= javascript_include_tag 'gloader', 'effects', 'controls', 'addresschooser/proxy/googlemap', 'addresschooser/addresschooser', 'application' %>
GLoader? What’s that ?
GLoader is a tiny javascript files that take care of loading my required libraries, hosted by Google :
NOTE : ScriptAculoUs is not loaded this way as it can’t be loaded properly right now. To help getting this fixed please review, comments, rate, ... the following issue : google.load / auto-loading and scriptaculous modules .
// Load libraries google.load("maps", "2"); google.load("prototype", "1.6.0.3"); // on page load complete, initialize the application google.setOnLoadCallback(function() { $(document.body).observe('onunload', GUnload); new Application; });
Add autocomplete CSS
Just add <%= stylesheet_link_tag 'autocomplete' %>
in the head of your page
Update your HTML
Now we got our required javascripts files loaded, let’s update our HTML to support Maptimize.AddressChooser
You’ll need to add the following fields and divs to your creation form
<div id='suggests' class='auto_complete' style='display:none'></div> <div id="map" style="position: absolute; top: 10px; left: 400px; height: 350px; width: 350px;"></div> <%= f.hidden_field :lat %> <%= f.hidden_field :lng %>
While editing, we’ll need some slightly different fields to be able to stay unobtrusive but also take full advantage of the client side geolocalization.
<div id='suggests' class='auto_complete' style='display:none'></div> <div id="map" style="position: absolute; top: 10px; left: 400px; height: 350px; width: 350px;"></div> <%= f.hidden_field :current_lat, :value => @business.lat %> <%= f.hidden_field :current_lng, :value => @business.lng %>
Initialize Maptimize.AddressChooser
Now we’ve got the HTML ready, our CSS and the required libraries let’s initialize Maptimize.AddressChooser to turn on the magic.
I’ve made couple of changes to work nicely in an unobtrusive way and while creating or editing any entry
From the demo application :
Application = Class.create({ initialize: function() { this.initAddressChooser('business', {street: 'address'}); }, initAddressChooser: function(object, options) { // Init options options = $H({ street: 'street', submit: 'submit', lat: 'lat', lng: 'lng', current_lat: 'current_lat', current_lng: 'current_lng', suggests: 'suggests' }).merge(options); // Check if current lat/lng are defined to use them var current_lat, current_lng; if ((current_lat = $(object+'_'+options.get('current_lat'))) && (current_lng = $(object+'_'+options.get('current_lng')))) current_lng.insert({after: '<input type="hidden" id="'+object+'_'+options.get('lat')+'" name="'+object+'['+options.get('lat')+']" value="'+current_lat.value+'" /><input type="hidden" id="'+object+'_'+options.get('lng')+'" name="'+object+'['+options.get('lng')+']" value="'+current_lng.value+'" />'}); var street, submit; if (!(street = $(object+'_'+options.get('street'))) || !(submit = $(object+'_'+options.get('submit')))) return(this); // BEGIN AUTOCOMPLETE SETTINGS AND HACKS :) // Create a local autocomplete without data. Data will be added dynamically according to map suggestions var autocomplete = new Autocompleter.Local(street, options.get('suggests'), [], { afterUpdateElement: function(element, selectedElement) { var index = selectedElement.up().immediateDescendants().indexOf(selectedElement); widget.showPlacemark(index); }, selector: function(instance) { instance.changed = false; return "<ul><li>" + instance.options.array.join('</li><li>') + "</li></ul>"; } } ); // Do not observe keyboard event autocomplete.onObserverEvent = function() {} // Wrap render to update map with selected placemarks autocomplete.render = autocomplete.render.wrap(function(method) { method(); widget.showPlacemark(this.index); }); // END AUTOCOMPLETE SETTINGS AND HACKS :) widget = new Maptimize.AddressChooser.Widget( { onInitialized: function(widget) { // Add default controls widget.getMap().setUIToDefault(); widget.initMap(); // Observe 'suggests:started' to display spinner and disable submit button widget.addEventListener('suggests:started', function() { street.addClassName('spinner'); }); // Observe 'suggests:found' to hide spinner and enable submit button if a placemark has been found widget.addEventListener('suggests:found', function(placemarks) { street.removeClassName('spinner'); street.focus(); // Reset autocomplete suggestions to new placemarks autocomplete.options.array.clear(); if (placemarks && placemarks.length > 0) { for (var i = 0; i < placemarks.length; i++) { autocomplete.options.array.push(widget.getAddress(placemarks[i])); } // For autocomplete update autocomplete.getUpdatedChoices(); autocomplete.show(); } else { autocomplete.hide(); } }); street.focus(); }, street: object+'_'+options.get('street'), lat: object+'_'+options.get('lat'), lng: object+'_'+options.get('lng') } ); return(this); } });
3. Take advantage of the front side geolocalization
For this purpose, we have to update our model to update the cached address when needed but also prevent from using the geolocalization on server-side if the client have already done it on front-side.
class Business < ActiveRecord::Base attr_accessor :current_lat, :current_lng acts_as_mappable before_validation :geocode_address private def geocode_address if (self.geolocalized_address != self.address) if (self.current_lat == self.lat && self.current_lng == self.lng) geo=Geokit::Geocoders::MultiGeocoder.geocode(address) errors.add(:address, "Could not Geocode address") if !geo.success self.lat, self.lng, self.geolocalized_address = geo.lat, geo.lng, self.address if geo.success else self.geolocalized_address = self.address end end end end
You will notice the 2 attributes accessors that will allow to check any client changes on the coordinates.
4. Map you object and keep optimized using Maptimize
When the time comes to work with maps (google maps in my case) and lot of markers, a problem comes up as you grow, TOO MUCH MARKERS!
Too much markers makes you maps loading horribly slow!
For this reasons, the experts at Xilinus came up with a elegant solution to deal with this, Maptimize
Maptimize take care of merging your markers as clusters to reduce your amount of visible items. That keep it much lighter to load but also nicer for the viewer.
I then recommend using Maptimize from the early stage. That keep your application easier to scale but also nicer to the end users.
Signup and config Maptimize
Once signed up, go in the “Security Settings” tab to get your MAP and API KEYS.
From here you could add this keys in your environment’s configuration but I feel that using a dedicated initializer will keep things cleaner and easier to maintain.
In the file config/initializers/maptimize_config.rb with :
# You'll need to add a cronjob to automatically synchronize your data with Maptimize running the following command line # # IMPORTANT: This will overwrite any file called *maptimize.csv* located in your app's tmp folder # If you like to change this behavior update lib/tasks/maptimize.rake # # rake maptimize:sync MAPTIMIZE_CSV_URL = "http://localhost:3000/businesses.csv" MAPTIMIZE_CSV_URL.freeze MAPTIMIZE_AUTHENTICITY_TOKEN = "AUTHENTICITY_TOKEN" MAPTIMIZE_AUTHENTICITY_TOKEN.freeze MAPTIMIZE_MAP_KEY = Rails.env.development? ? "DEVELOPMENT_KEY" : "PRODUCTION_KEY" MAPTIMIZE_MAP_KEY.freeze
Send your data to Maptimize
Create a CSV file generator
Thanks to rails, is this trivial.
Update your controller
In your controller update your index action, from the demo “businesses_controller.rb” :
# GET /businesses # GET /businesses.xml # GET /businesses.csv def index @businesses = Business.all respond_to do |format| format.html # index.html.erb format.xml { render :xml => @businesses } format.csv # index.csv.erb end end
Add “app/views/businesses/index.csv.erb“
id,lat,lng <%- @businesses.each do |business| -%> <%= business.id.to_s + ',' + business.lat.to_s + ',' + business.lng.to_s %> <%- end -%>
Well, that’s done! If you check http://localhost:3000/businesses.csv you’ll see a simple CSV file containing the minimum data required by Maptimize
Send your CSV file to Maptimize
This is something that you may like to run in a cronjob to keep your data synchronized automatically.
From a bash shell console, just run :
IMPORTANT: This will overwrite any file called “maptimize.csv” located in your app’s tmp folder If you like to change this behavior update lib/tasks/maptimize.rake
rake maptimize:sync
If everything worked as expected, your command should ends with :
<?xml version="1.0" encoding="UTF-8"?> <import> <status>succeeded</status> </import>
Show your objects on a map
Now that Maptimize have our data, it’s time to use them and get our map
Include the CSS and Javascript files required to integrate Maptimize
In your head section :<link href="http://www.maptimize.com/stylesheets/cluster.css" rel="stylesheet" type="text/css" />In the bottom of your page, right after Google jsapi file
<script src="http://www.maptimize.com/api/v1/<%= MAPTIMIZE_MAP_KEY %>/embed.js" type="text/javascript"></script>
Display your map
Add a div that will contain your map
In the demo, I’ve added the div in the index file “app/views/businesses/index.html.erb
<div id="map" style="width: 900px; height: 500px;"></div>
Initialize the map
In my case I’ll update “public/javascripts/application.js” to include :
Application = Class.create({ initialize: function() { this.initAddressChooser('business', {street: 'address'}) .initMap('map'); }, initAddressChooser: function(object, options) { ... return(this); }, initMap: function(map) { if (!GBrowserIsCompatible() || !(map = $(map))) return(this); if (typeof Maptimize.Map == "undefined") { map.update("Maptimize key is not correctly set, check file config/initializers/maptimize_config.rb or \ run 'rake bootstrap' to fill database with some data"); } else { // Create a new google map var map = new GMap2(map); // Center on the world map.setCenter(new GLatLng(47, 1), 2); // Add controls map.addControl(new GSmallMapControl()); // Attach maptimize service window.maptimizeMap = new Maptimize.Map(map); } return(this); } });
Show me the real deal!
The see the goodness of Maptimize you need to have a good bunch of entry, for this purpose you can run :
rake bootstrap
It will load 500 entries in your database.
Done?
That’s it! we have a map with all our objects localized, merged when needed, ... it can’t get better!
5. Show details when clicking on markers and clusters
“it can’t get better!” I said? Well it can!
What about showing our object details when we click a marker? Let’s do it!
Update our Maptimize initialization to bind actions on click for clusters and markers
Application = Class.create({ ... initMap: function(map) { if (!GBrowserIsCompatible() || !(map = $(map))) return(this); if (typeof Maptimize.Map == "undefined") { map.update("Maptimize key is not correctly set, check file config/initializers/maptimize_config.rb or \ run 'rake bootstrap' to fill database with some data"); } else { // Create a new google map var map = new GMap2(map); // Center on the world map.setCenter(new GLatLng(47, 1), 2); // Add controls map.addControl(new GSmallMapControl()); // Attach maptimize service var app = this; window.maptimizeMap = new Maptimize.Map(map, { onMarkerClicked: function(marker) { return app.getMarkerDetails(marker, marker.getId()); }, onZoomMaxClusterClicked: function(cluster, ids) { return app.getMarkerDetails(cluster, ids); } }); } return(this); }, getMarkerDetails: function(object, ids) { return new Ajax.Request("/businesses/"+ids, { method: 'GET', onComplete: function(response) { object.getGMarker().openInfoWindowHtml(response.responseText); } }); } });
As you can see, the id of the marker will be used to call the show action of our object, “business” in this case.
What append when you click a cluster, the id string looks like “1,2,3“
For this we will update our action to support multiple ids and show the expected details
Update our controller
Looking at “app/controllers/businesses_controller.rb” we’ll change the show method as follow :
# GET /businesses/1 # GET /businesses/1.xml # GET /businesses/1,2 def show @businesses = Business.find(params[:id].split(',')) @business = @businesses.first respond_to do |format| format.html # show.html.erb format.xml { render :xml => @business } format.js # show.js.erb end end
The split call will allow us to get more IDS at once, to keep our initial formats working properly I keep the single object as well.
Add the view “app/views/businesses/show.js.erb“
<%- @businesses.each do |business| -%> <p><%= business.name %><br /><small><%= business.address %></small></p> <%- end -%>
6. Bonus, use Maptimize makers when creating / editing entries
To use the Maptimized map with markers when creating / editing your entries, just do the tiny change in initAddressChooser from “/public/application.js“
Include this code just after widget.getMap().setUIToDefault();
// Attach maptimize plugin var maptimizeMap = new Maptimize.Map(widget.getMap());
AJAX InPlaceRichEditor 1.3
As of today the first release candidate of the AJAX InPlaceRichEditor version 1.3 have seen the public scene.
Update: Version 1.3.2: With TinyMCE 3.4.2, Prototype 1.7 and Scriptaculous 1.9.0. Add support for Internet Explorer 9
This new release is now using the fantastic TinyMCE 3.0 from MoxieCode
It include some new features and improvements, some wished ones finally achieved ;-) so let's discover them.
Multiple TinyMCE initializations
Now you need to give an extra parameter to AJAX.InPlaceRichEditor constructor, containing the TinyMCE options you wish to use.
The best way to do so is to keep all the initialization in the tiny_mce_init file as it may preload all needed modules and make InPlaceRichEdition more responsive, let's see what it mean.
In tiny_mce_init,js
var tinymce_options = { mode : "textareas", theme : "simple" }; var tinymce_advanced_options = { mode : "textareas", theme : "advanced" }; tinyMCE.init(tinymce_advanced_options); tinyMCE.init(tinymce_options);
NOTICE: It's important to take care of the initialization order as the last one will be used by default for the normal editors.
Your AJAX.InPlaceRichEditor calls
<h1 id="tobeedited">To be edited w/ simple theme</h1> <script> // <![CDATA[ new Ajax.InPlaceRichEditor($('tobeedited'), 'YOU_UPDATE_URL', {}, tinymce_options); // ]]> </script> <h1 id="tobeeditedadvanced">To be edited w/ advanced theme</h1> <script> // <![CDATA[ new Ajax.InPlaceRichEditor($('tobeeditedadvanced'), 'YOU_UPDATE_URL', {}, tinymce_advanced_options); // ]]> </script>
Features fixes and changes
- The fieldPostCreation option is now properly working on InPlaceRichEditor side and is set to focus by default, however there's still some bugs with it in some browsers.
- As in InPlaceRichEditor 1.2, the deprecation layer from scriptaculous' InPlaceEditoris ot present anymore, I believe that it's now the time to use the actual stuff in your scripts and extensions ;-)
- The RichEditor will be disabled while loading external text if you are using the advanced theme, the simple one doesn't seem to allow it
Highly recomanded patch
In scriptaculous' InPlaceEditor 1.8.1 the failure handling is broken which doesn't help providing clean error explanations to users.
I brought few week ago a tiny patch to fix this InPlaceEditor onFailure issue that I highly recommend to all users.
Extra feature as InPlaceEditor patch
I needed for Tiare the ability to have the empty texts in edit mode for easier comprehension, for this reason I've published a patch to editOnBlank an InPlaceEditor or InPlaceRichEditor.
Rails 2.0 users' happiness
In Rails 2.0 a new security feature introduce an authenticity_token to every form and InPlaceRichEditor can work fine with this as well.
Even if it's still not an easy-as-pie feature, it's pretty easy to build a work around for it.
Railers, grab this InPlaceRichEditor 1.3_rc0 with rails 2.0.2 demo to find more about it.
Security notice
DO NEVER FORGET to clean code sent by InPlaceRichEditor, javascript cleaning is wonderful but purely useless when we speak about security.
Railers may consider using white_list but you can also use what ever way you like to keep yourself secure.
Tested
InPlaceRichEditor has been tested on FF 2, IE 6, IE 7, Safari 3 and Opera, all on Mac (except IE) and Windows
Some bugs relative to fieldPostCreation are still present on some browser
Thanks
Thank you to all contributors, from feedbacks to patches, it's always a pleasure to listen about your experience using InPlaceRichEditor.
A big thanks as well to the prototype and scriptaculous core team and contributors.
Of course big thanks to MoxieCode too for them great tool and support
Have fun
Patch for InPlaceEditor adding editOnBlank feature
Since a while I'm running on own patched version of InPlaceEditor for Tiare, mainly to support editOnBlank which allow an empty container to be turned as editable just after initialization.
It turned to be useful as well if you accept blank response as it will keep in edit mode in this case
Notice
Please be aware that it's patching the actual version of InPlaceEditor, actually at revision 8787 on script.aculo.us 1.8.1 and it can make more damages on previous or later versions.
How to use
... <head> ... <script src="javascripts/prototype.js" type="text/javascript"></script> <script src="javascripts/control.js" type="text/javascript"></script> <script src="javascripts/patch_inplaceeditor_1-8-1.js" type="text/javascript"></script> ... </head> ...
Advise
I advise you to use the InPlaceEditor's failure patch as well.
Download
InPlaceEditor's editOnBlank extension
Have fun ;-)
Temporary patch to InPlaceEditor failure issues
Working one the new InPlaceRichEditor version I first made a closer look again at InPlaceEditor from scriptaculous and found 2 bugs on the failure handling.
I first made a patch a help the core team fixing it but as I needed to apply this fix right away for my own use I made a small extension fixing this issues.
Notice
Please be aware that it's patching the actual version of InPlaceEditor, actually at revision 8787 on script.aculo.us 1.8.1 and it can make more damages on previous or later versions.
How to use
You just need to include the script after the original InPlaceEditor script
... <head> ... <script src="javascripts/prototype.js" type="text/javascript"></script> <script src="javascripts/control.js" type="text/javascript"></script> <script src="javascripts/patch_inplaceeditor_1-8-1.js" type="text/javascript"></script> ... </head> ...
UPDATE: You may also like to use the feature editOnBlank
Have fun ;-)
InPlaceRichEditor V 1.2 released
Hi,
I'm pleased to announce InPlaceRichEditor v1.2
For more details please refer to the download section
It's now time to work on the v1.3_rc0 using TinyMCE 3.0 ;-)
ZenCocoon got refreshed!
I just updated ZenCocoon.com with new services and a new design.
Rails Plugin : Multi Site : Updated
Hi,
I've find out that the plugin multi_site I made broke when using find(:include). Well it's now fix and ready to use.
http://box.svnrepository.com/svn/plugins/multi_site/
Have fun ;-)
InPlaceRichEditor V 1.1.2 released
Hi,
Minor release fixing the <p> stripped out bug.
Enjoy it
InPlaceRichEditor V 1.1.1 released
Hi all,
Just few days after the version 1.1 released I may like to announce the release of the version 1.1.1 fixing a bug when loading external text (Thanks to Robert Muzslai).
I've also added to the website a version feed to keep looking at the new release easier.
http://inplacericheditor.box.re
Have fun ;-)
InPlaceRichEditor v1.1 released
Hi,
InPlaceRichEditor v1.1 has been released yesterday and can be downloaded on: http://inplacericheditor.box.re/download
Couple of changes has been made in InPlaceRichEditor fixing ShowSaving bug and changing the call to InPlaceRichEditor itself.
Please notice that this last change break the backward compatibility since initial release.
Read more about it on: http://inplacericheditor.box.re/download
Have fun !
InPlaceRichEditor: AJAX-powered WYSIWYG editor
I saw couple of person looking for a script to put on top of script.aculo.us ’ InPlaceEditor and has I also had this need I brought something up called InPlaceRichEditor
You can find more about it on http://inplacericheditor.box.re
It actually had been tested and works on FF1.5, FF2, IE 6, IE7, Opera 9.1
The site contain actually just few infos about it but gonna be improve soon. Please does not hesitate If you have any questions, comments, improvements, tips, ...
Simplify Rails configuration for sending email
Hi,
We got great thing for configuration of the database but as I know it’s harder to do the same for sending email.
Here is a simple library, load_email_configuration.rb that may help to keep the basic settings out of the rest of the code, as database.yml do.
Once you put this library in the right place, lib/ you’ll need to include it in your config/environment.rb file
# Load email configuration require 'load_email_configuration'
If you checked the library source (if you didn’t, you should ;-)) you see that it’s gonna load the file config/email.yml and will load different settings depending on the environment.
There is an example of email.yml file : email.example.yml
Once you get the library included and your config/email.yml file properly modified you got to be sure that ActionMailer is well set.
In my case I use such configuration for development (so in config/environments/development.rb:
# Care if the mailer can't send config.action_mailer.raise_delivery_errors = true # Use SMTP protocol to deliver emails config.action_mailer.delivery_method = :smtp # Don't send emails config.action_mailer.perform_deliveries = false
and this one for production (so in config/environments/production.rb:
# Disable delivery errors if you bad email addresses should just be ignored config.action_mailer.raise_delivery_errors = false # Use SMTP protocol to deliver emails config.action_mailer.delivery_method = :smtp
For more about ActionMailer configuration please read the api documentation
Now you did all this things, don’t forget to restart your server ;-)
UPDATE : On shared host using FCGI you should just wait few minutes for the changes to take effects, if that still not working try killall -usr1 dispatch.fcgi
Have fun, and do NOT spam ;-)
validates_type_of
I was wondering how I could validates the type of an object before creating or updating it.
As I didn’t find any already built tool for it, I made a small library that allow you to do such things
in your model:
Validates only if string_require.type is a String:
validates_type_of :string_require, :type => String
Validates only if float_require.type is a Float:
validates_type_of :float_require, :type => Float
And so on…
To use it you might need to create the following file:
File /lib/validates_type_of.rb:
module ActiveRecord
class Errors
@@default_error_messages[:invalid_type] ||= "wrong type. %s expected"
end
module Validations
module ClassMethods
# Validates whether the value of the specified object is from the correct type
#
# class Tariff < ActiveRecord::Base
# validates_type_of :price, :type => Float
# end
#
# Configuration options:
# * <tt>type</tt> - The acceptable type
# * <tt>message</tt> - A custom error message (default is: "wrong type. %s expected")
def validates_type_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid_type], :on => :save }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
# Ensure that the type is secified
raise(ArgumentError, 'Type unspecified.') if configuration[:type].nil?
message = configuration[:message] % configuration[:type]
validates_each(attr_names, configuration) do |record, attr_name, value|
if !value.blank?
record.errors.add(attr_name, message) if !record.send("#{attr_name}_before_type_cast").is_a?(configuration[:type])
end
end
end
end
end
end
Now you will need to load at the end of your /config/environment.rb like this:
require 'validates_type_of'
Please be conscious that the use of this library is to your own risk, maybe to understand all it does might help you using it properly without dommaging your application.
Cheers!
validates_method_of
I personally was looking for a way to validate a specific object method in a model.
In my case I would for exemple to allow only an admin or owner to post an article.
For this I made an extention to the active record ’s validations class methods to allow me to do this simply and with flexibility.
validates_method_of.rb: (updated after Jean-François comments)module ActiveRecord module Validations module ClassMethods # Validates whether the value of the specified object method is correct from matching # one of the accepted values provided or if different than the excepted one # # class Article < ActiveRecord::Base # validates_method_of :user, :method => 'role', :only => %w( owner admin ) # end # # Configuration options: # * <tt>method</tt> - The method that will be validate # * <tt>message</tt> - A custom error message (default is: "not authorized") # * <tt>only</tt> - An enumerable object of items that the value should be part of # * <tt>except</tt> - An enumerable object of items that the value shouldn't be part of def validates_method_of(*attr_names) configuration = { :message => 'not authorized', :on => :save } configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash) if configuration[:only] enum = configuration[:only] validates_each(attr_names, configuration) do |record, attr_name, value| if !value.blank? method = value.send(configuration[:method].to_s) record.errors.add(attr_name, configuration[:message]) if !enum.include?(method) end end else enum = configuration[:except] validates_each(attr_names, configuration) do |record, attr_name, value| if !value.blank? method = value.send(configuration[:method].to_s) record.errors.add(attr_name, configuration[:message]) if enum.include?(method) end end end end end end end
I personally put this file in my lib directory and loaded it from config/environment.rb like this:
require 'validates_method_of'