direct_address 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+
23
+ *.yml
24
+ *.log
25
+ *.gem
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Mike Nelson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,87 @@
1
+ = direct_address
2
+
3
+ Direct Address provides a rails app with simple address features. This is a streamlined implementation of address functionality. Direct Address provides you with address, country, and region classes. It also provides a generator which generates the necessary javascript and rake tasks to implement properly. You'll also get form helpers to easily implement Direct Address in your views. A rake task is provided that allows up to date country and region information to be downloaded from geoname.org.
4
+
5
+ == Installation
6
+
7
+ Add direct_address to your environment.rb
8
+
9
+ config.gem 'direct_address'
10
+
11
+ Then install the gem:
12
+
13
+ rake gems:install
14
+
15
+ After you've installed the gem, you'll need to generate your classes, javascript, and rake task.
16
+
17
+ script/generate direct_address
18
+
19
+ Once that's complete, migrate your database
20
+
21
+ rake db:migrate
22
+
23
+ Now you're ready to download up-to-date country and region information from geoname.org
24
+
25
+ rake direct_address:pull
26
+
27
+ <b>This will take a few minutes and should be done on a decent connection.</b>
28
+
29
+ == Usage
30
+
31
+ Once you've run the generators there's not much you need to do. You can utilize the classes as they are or you can use the acts_as_addressable option. To use acts_as_addressable, include it in your class like this:
32
+
33
+ class User < ActiveRecord::Base
34
+ acts_as_addressable
35
+ end
36
+
37
+ By doing this you are giving the user a single address. One thing to note is that the generator didn't create an addresses_controller or add any routes regarding addresses. By default, it is expected that the address form will be nested in the parent form. For this reason, the accepts_nested_attributes_for and attr_accessors are included in acts_as_addressable. If you'd like to implement things differently, create a controller and implement your models to your liking.
38
+
39
+ == Form Helpers
40
+
41
+ By including Direct Address in your project you've added a few form helpers. Their implementation is very easy. You can either use it within a form_for like this:
42
+
43
+ <% form_for @user do |user_form| %>
44
+ ...
45
+ <% user_form.fields_for :address do |address_form| %>
46
+ ...
47
+ <%= address_form.country_select %>
48
+ ...
49
+ <% end %>
50
+ ...
51
+ <% end %>
52
+
53
+ Or you can use it via a tag helper like this:
54
+
55
+ <%= country_select_tag 'user[account]' %>
56
+
57
+ Either way produces the same exact thing. Take note that this does require loading at least prototype *beforehand* and the country_select file before the page load is complete. If you do like to defer the loading of extra scripts, feel free to do so with country_select. The setup doesn't occur until the window.onload event is fired. An example load order would be:
58
+
59
+ <script type="text/javascript" src="javascripts/prototype.js"></script>
60
+
61
+ POINT A
62
+
63
+ ...
64
+ <% form... %>
65
+ <%= f.country_select %>
66
+ <% end %>
67
+ ...
68
+
69
+ POINT B
70
+
71
+ Where the following call could be at either POINT A or POINT B:
72
+
73
+ <script type="text/javascript" src="javascripts/country_select.js"></script>
74
+
75
+ == Note on Patches/Pull Requests
76
+
77
+ * Fork the project.
78
+ * Make your feature addition or bug fix.
79
+ * Add tests for it. This is important so I don't break it in a
80
+ future version unintentionally.
81
+ * Commit, do not mess with rakefile, version, or history.
82
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
83
+ * Send me a pull request. Bonus points for topic branches.
84
+
85
+ == Copyright
86
+
87
+ Copyright (c) 2010 Mike Nelson. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "direct_address"
8
+ gem.summary = "short summary"
9
+ gem.description = "long summary"
10
+ gem.email = "mn@mcaddie.com"
11
+ gem.homepage = "http://github.com/mnelson/direct_address"
12
+ gem.authors = ["Mike Nelson"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "direct_address #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
@@ -0,0 +1,76 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{direct_address}
8
+ s.version = "0.0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Mike Nelson"]
12
+ s.date = %q{2010-02-17}
13
+ s.description = %q{long summary}
14
+ s.email = %q{mn@mcaddie.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "direct_address-0.0.2.gem",
27
+ "direct_address.gemspec",
28
+ "generators/direct_address/direct_address_generator.rb",
29
+ "generators/direct_address/templates/controllers/regions_controller.rb",
30
+ "generators/direct_address/templates/js/country_select.js",
31
+ "generators/direct_address/templates/migrations/direct_address_migration.rb",
32
+ "generators/direct_address/templates/models/address.rb",
33
+ "generators/direct_address/templates/models/country.rb",
34
+ "generators/direct_address/templates/models/region.rb",
35
+ "generators/direct_address/templates/tasks/harvest.rake",
36
+ "generators/direct_address/templates/views/index.json.erb",
37
+ "lib/direct_address.rb",
38
+ "lib/direct_address/acts_as_addressable.rb",
39
+ "lib/direct_address/form_builder.rb",
40
+ "lib/direct_address/form_helper.rb",
41
+ "lib/direct_address/harvester/extend_string.rb",
42
+ "lib/direct_address/harvester/geoname.rb",
43
+ "spec/database.yml",
44
+ "spec/debug.log",
45
+ "spec/direct_address_spec.rb",
46
+ "spec/model/user.rb",
47
+ "spec/schema.rb",
48
+ "spec/spec.opts",
49
+ "spec/spec_helper.rb"
50
+ ]
51
+ s.homepage = %q{http://github.com/mnelson/direct_address}
52
+ s.rdoc_options = ["--charset=UTF-8"]
53
+ s.require_paths = ["lib"]
54
+ s.rubygems_version = %q{1.3.5}
55
+ s.summary = %q{short summary}
56
+ s.test_files = [
57
+ "spec/direct_address_spec.rb",
58
+ "spec/model/user.rb",
59
+ "spec/schema.rb",
60
+ "spec/spec_helper.rb"
61
+ ]
62
+
63
+ if s.respond_to? :specification_version then
64
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
65
+ s.specification_version = 3
66
+
67
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
68
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
69
+ else
70
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
71
+ end
72
+ else
73
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
74
+ end
75
+ end
76
+
@@ -0,0 +1,24 @@
1
+ class DirectAddressGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+
5
+ m.file "js/country_select.js", "public/javascripts/country_select.js"
6
+
7
+ m.directory "app/views/regions"
8
+ m.file "views/index.json.erb", "app/views/regions/index.json.erb"
9
+
10
+ m.file "controllers/regions_controller.rb", "app/controllers/regions_controller.rb"
11
+
12
+ %w(address country region).each do |model|
13
+ m.file "models/#{model}.rb", "app/models/#{model}.rb"
14
+ end
15
+
16
+ m.route_resources :regions
17
+
18
+ m.migration_template "migrations/direct_address_migration.rb", "db/migrate", {:migration_file_name => "direct_address_migration"}
19
+
20
+ m.file "tasks/harvest.rake", "lib/tasks/direct_address_harvest.rake"
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,10 @@
1
+ class RegionsController < ApplicationController
2
+
3
+ def index
4
+ @regions = params[:country_id] && Region.by_country(params[:country_id])
5
+ respond_to do |format|
6
+ format.json
7
+ end
8
+ end
9
+
10
+ end
@@ -0,0 +1,33 @@
1
+ if(typeof(Prototype) != 'undefined'){
2
+
3
+ CountrySelect = Class.create({
4
+ initialize : function(args){
5
+ this.country_select = $(args['country_select_id']);
6
+ this.region_select = $(args['region_select_id']);
7
+ this.country_select.observe('change', this.update_regions.bind(this));
8
+ },
9
+ update_regions : function(event){
10
+ this.region_select.selectedIndex = -1;
11
+ var country_id = this.selected_country()[0];
12
+ new Ajax.Request('/regions.json?country_id=' + country_id, {method : 'get', onComplete : function(transport){
13
+ var regions = transport.responseJSON;
14
+ this.populate_regions(regions);
15
+ }.bind(this)});
16
+ },
17
+ selected_country : function(){
18
+ var elem = this.country_select.childElements()[this.country_select.selectedIndex];
19
+ return [elem.value, elem.innerHTML]
20
+ },
21
+ populate_regions : function(regions){
22
+ var html = ['<option value="">Select a Region</option>', regions.collect(function(region){
23
+ return '<option value="' + region.id + '">' + region.name + '</option>';
24
+ })].flatten().join('');
25
+ this.region_select.update(html);
26
+ if(regions.length == 0)
27
+ this.region_select.hide();
28
+ else
29
+ this.region_select.show();
30
+ }
31
+ });
32
+
33
+ } else { try{ console.log('Prototype must be preloaded for country_select.js to work properly.'); } catch (e) {}}
@@ -0,0 +1,39 @@
1
+ class DirectAddressMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :regions do |t|
4
+ t.references :country
5
+ t.string :name
6
+ t.timestamps
7
+ end
8
+
9
+ add_index :regions, :country_id
10
+
11
+ create_table :addresses do |t|
12
+ t.references :addressable, :polymorphic => true, :null => false
13
+ t.references :country
14
+ t.references :region
15
+ t.string :street1
16
+ t.string :street2
17
+ t.string :city
18
+ t.string :postal
19
+ t.timestamps
20
+ end
21
+
22
+ add_index :addresses, [:addressable_id, :addressable_type]
23
+ add_index :addresses, [:country_id]
24
+ add_index :addresses, [:region_id]
25
+
26
+ create_table :countries do |t|
27
+ t.string :abbreviation
28
+ t.string :name
29
+ t.timestamps
30
+ end
31
+
32
+ end
33
+
34
+ def self.down
35
+ drop_table :regions
36
+ drop_table :countries
37
+ drop_table :addresses
38
+ end
39
+ end
@@ -0,0 +1,24 @@
1
+ class Address < ActiveRecord::Base
2
+
3
+ belongs_to :addressable, :polymorphic => true
4
+ belongs_to :country
5
+ belongs_to :region
6
+
7
+
8
+ def single_line
9
+ multiline.join(', ')
10
+ end
11
+
12
+ def multiline
13
+ [street1, street2, [city, region_name, postal].compact.join(' '), country_abbrev].compact.collect{|s| s.length > 0 && s}
14
+ end
15
+
16
+ def region_name
17
+ self.region && self.region.name || nil
18
+ end
19
+
20
+ def country_abbrev
21
+ self.country && self.country.abbrev || nil
22
+ end
23
+
24
+ end
@@ -0,0 +1,18 @@
1
+ class Country < ActiveRecord::Base
2
+
3
+ has_many :regions
4
+
5
+ validates_presence_of :abbreviation, :name
6
+ validates_length_of :abbreviation, :is => 2
7
+ alias_attribute :abbrev, :abbreviation
8
+
9
+ default_scope :order => 'name ASC'
10
+
11
+ before_create :format_abbreviation
12
+
13
+ private
14
+
15
+ def format_abbreviation
16
+ self.abbreviation = self.abbreviation.upcase
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ class Region < ActiveRecord::Base
2
+
3
+ belongs_to :country
4
+ validates_presence_of :name, :country
5
+
6
+ default_scope :order => 'name ASC'
7
+
8
+ named_scope :by_country, lambda {|country| country && (country.is_a?(Country) && (country = country.id) || true) && {:conditions => ['country_id = ?', country]} || {}}
9
+ end
@@ -0,0 +1,13 @@
1
+
2
+ begin
3
+ require File.dirname(__FILE__) + '/../../config/environment.rb'
4
+
5
+ namespace :direct_address do
6
+ desc "pulls up to date country and region data from geonames.org"
7
+ task :pull do
8
+ Geoname.retrieve_to_db
9
+ end
10
+ end
11
+ rescue
12
+ puts "Direct address harvester not properly installed"
13
+ end
@@ -0,0 +1 @@
1
+ <%= @regions.collect{|region| {:id => region.id, :name => region.name}}.to_json %>
@@ -0,0 +1,20 @@
1
+ module Direct
2
+ module Address
3
+
4
+ def self.included(base)
5
+ base.extend ::Direct::Address::ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def acts_as_addressable
11
+ has_one :address, :as => :addressable
12
+ accepts_nested_attributes_for :address
13
+ attr_accessible :address_attributes
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+
20
+ ::ActiveRecord::Base.send(:include, ::Direct::Address)
@@ -0,0 +1,25 @@
1
+ module ActionView
2
+ module Helpers
3
+ module CountryAndRegion
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+ include ActionView::Helpers::CountryAndRegion::InstanceMethods
8
+ end
9
+ end
10
+
11
+ module InstanceMethods
12
+ def country_select
13
+ @countries ||= Country.all
14
+ sanitized_object_id = self.object_name.split('[').collect{|s| (s = s.gsub(']','')) && s.size > 0 && s || nil}.compact.join('_')
15
+ html = self.select(:country_id, ([['Select a Country', nil]] + @countries.collect{|country| [country.name, country.id]}))
16
+ html << self.select(:region_id, ([['Select a Region', nil]] + (self.object && self.object.country && self.object.country.regions.collect{|region| [region.name, region.id]} || [])))
17
+ html << "<script type=\"text/javascript\">if(typeof(Prototype) != 'undefined'){Element.observe(window, 'load', function(){if(typeof(CountrySelect) == 'undefined'){try{console.log('country_select.js not loaded.');} catch (e) {};return;};new CountrySelect({country_select_id : '#{sanitized_object_id}_country_id', region_select_id : '#{sanitized_object_id}_region_id'})});}else{document.getElementById('#{sanitized_object_id}_country_id').style.display = 'none';document.getElementById('#{sanitized_object_id}_region_id').style.display = 'none';}</script>"
18
+ html
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ ::ActionView::Helpers::FormBuilder.send :include, ActionView::Helpers::CountryAndRegion
@@ -0,0 +1,30 @@
1
+
2
+ module ActionView
3
+ module Helpers
4
+ module CountryAndRegionHelper
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ include ActionView::Helpers::CountryAndRegionHelper::InstanceMethods
9
+ end
10
+ end
11
+
12
+ module InstanceMethods
13
+ def country_select_tag(object_string, country_id = nil, region_id = nil)
14
+ object_string = object_string.to_s
15
+ object_id = object_string.split('[').collect{|s| (s = s.gsub(']','')) && s.size > 0 && s || nil}.compact.join('_')
16
+ @countries ||= Country.all
17
+ @regions = !country_id.nil? && Region.by_country(country_id) || []
18
+ country_options = options_for_select([['Select a Country', nil]] + @countries.collect{|country| [country.name, country.id]}, country_id)
19
+ region_options = options_for_select([['Select a Region', nil]] + @regions.collect{|region| [region.name, region.id]}, region_id)
20
+ html = select_tag("#{object_string}[country_id]", country_options, :id => "#{object_id}_country_id")
21
+ html << select_tag("#{object_string}[region_id]", region_options, :id => "#{object_id}_region_id")
22
+ html << "<script type=\"text/javascript\">if(typeof(Prototype) != 'undefined'){Element.observe(window, 'load', function(){if(typeof(CountrySelect) == 'undefined'){try{console.log('country_select.js not loaded.');} catch (e) {};return;};new CountrySelect({country_select_id : '#{object_id}_country_id', region_select_id : '#{object_id}_region_id'});});}else{document.getElementById('#{object_id}_country_id').style.display = 'none';document.getElementById('#{object_id}_region_id').style.display = 'none';}</script>"
23
+ html
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ ::ActionView::Base.send :include, ActionView::Helpers::CountryAndRegionHelper
@@ -0,0 +1,85 @@
1
+ # RemoveAccents version 1.0.3 (c) 2008-2009 Solutions Informatiques Techniconseils inc.
2
+ #
3
+ # This module adds 2 methods to the string class.
4
+ # Up-to-date version and documentation available at:
5
+ #
6
+ # http://www.techniconseils.ca/en/scripts-remove-accents-ruby.php
7
+ #
8
+ # This script is available under the following license :
9
+ # Creative Commons Attribution-Share Alike 2.5.
10
+ #
11
+ # See full license and details at :
12
+ # http://creativecommons.org/licenses/by-sa/2.5/ca/
13
+ #
14
+ # Version history:
15
+ # * 1.0.3 : July 23 2009
16
+ # Corrected some incorrect character codes. Source is now wikipedia at:
17
+ # http://en.wikipedia.org/wiki/ISO/IEC_8859-1#Related_character_maps
18
+ # Thanks to Raimon Fernandez for pointing out the incorrect codes.
19
+ # * 1.0.2 : October 29 2008
20
+ # Slightly optimized version of urlize - Jonathan Grenier (jgrenier@techniconseils.ca)
21
+ # * 1.0.1 : October 29 2008
22
+ # First public revision - Jonathan Grenier (jgrenier@techniconseils.ca)
23
+ #
24
+
25
+ class String
26
+ # The extended characters map used by removeaccents. The accented characters
27
+ # are coded here using their numerical equivalent to sidestep encoding issues.
28
+ # These correspond to ISO-8859-1 encoding.
29
+ ACCENTS_MAPPING = {
30
+ 'E' => [200,201,202,203],
31
+ 'e' => [232,233,234,235],
32
+ 'A' => [192,193,194,195,196,197],
33
+ 'a' => [224,225,226,227,228,229,230],
34
+ 'C' => [199],
35
+ 'c' => [231],
36
+ 'O' => [210,211,212,213,214,216],
37
+ 'o' => [242,243,244,245,246,248],
38
+ 'I' => [204,205,206,207],
39
+ 'i' => [236,237,238,239],
40
+ 'U' => [217,218,219,220],
41
+ 'u' => [249,250,251,252],
42
+ 'N' => [209],
43
+ 'n' => [241],
44
+ 'Y' => [221],
45
+ 'y' => [253,255],
46
+ 'AE' => [306],
47
+ 'ae' => [346],
48
+ 'OE' => [188],
49
+ 'oe' => [189]
50
+ }
51
+
52
+
53
+ # Remove the accents from the string. Uses String::ACCENTS_MAPPING as the source map.
54
+ def removeaccents
55
+ str = String.new(self)
56
+ String::ACCENTS_MAPPING.each {|letter,accents|
57
+ packed = accents.pack('U*')
58
+ rxp = Regexp.new("[#{packed}]", nil, 'U')
59
+ str.gsub!(rxp, letter)
60
+ }
61
+
62
+ str
63
+ end
64
+
65
+
66
+ # Convert a string to a format suitable for a URL without ever using escaped characters.
67
+ # It calls strip, removeaccents, downcase (optional) then removes the spaces (optional)
68
+ # and finally removes any characters matching the default regexp (/[^-_A-Za-z0-9]/).
69
+ #
70
+ # Options
71
+ #
72
+ # * :downcase => call downcase on the string (defaults to true)
73
+ # * :convert_spaces => Convert space to underscore (defaults to false)
74
+ # * :regexp => The regexp matching characters that will be converting to an empty string (defaults to /[^-_A-Za-z0-9]/)
75
+ def urlize(options = {})
76
+ options[:downcase] ||= true
77
+ options[:convert_spaces] ||= false
78
+ options[:regexp] ||= /[^-_A-Za-z0-9]/
79
+
80
+ str = self.strip.removeaccents
81
+ str.downcase! if options[:downcase]
82
+ str.gsub!(/\ /,'_') if options[:convert_spaces]
83
+ str.gsub(options[:regexp], '')
84
+ end
85
+ end