mir_utility 0.3.29
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/Manifest +6 -0
- data/README.rdoc +32 -0
- data/Rakefile +19 -0
- data/init.rb +1 -0
- data/lib/mir_form_builder.rb +176 -0
- data/lib/mir_utility.rb +853 -0
- data/mir_utility.gemspec +30 -0
- metadata +81 -0
data/Manifest
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
= Mir Utility
|
2
|
+
|
3
|
+
Standard extensions for Mir Rails apps.
|
4
|
+
|
5
|
+
== Prerequisites
|
6
|
+
|
7
|
+
sudo gem install echoe
|
8
|
+
|
9
|
+
== Install
|
10
|
+
|
11
|
+
Clone the git repo:
|
12
|
+
|
13
|
+
git clone git@github.com:Bantik/MirUtility.git
|
14
|
+
|
15
|
+
To build from your local source:
|
16
|
+
|
17
|
+
rake manifest
|
18
|
+
rake build_gemspec
|
19
|
+
gem build mir_utility.gemspec
|
20
|
+
sudo gem install mir_utility
|
21
|
+
|
22
|
+
== Usage
|
23
|
+
|
24
|
+
See the RDocs.
|
25
|
+
|
26
|
+
== Revising the Gem
|
27
|
+
|
28
|
+
* Make your changes.
|
29
|
+
* Test locally with rspec.
|
30
|
+
* Revise the version number in Rakefile.
|
31
|
+
* Follow build-from-local-source instructions above.
|
32
|
+
* Commit and push your changes.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/testtask'
|
6
|
+
require 'rake/rdoctask'
|
7
|
+
require 'tasks/rails'
|
8
|
+
require 'echoe'
|
9
|
+
|
10
|
+
Echoe.new('mir_utility', '0.3.29') do |p|
|
11
|
+
p.description = "Standard extensions for Mir Rails apps."
|
12
|
+
p.url = "http://github.com/bantik/mir_utility"
|
13
|
+
p.author = "Corey Ehmke and Rod Monje"
|
14
|
+
p.email = "corey@seologic.com"
|
15
|
+
p.ignore_pattern = ["app/**/*", "config/**/*", "db/**/*", "lib/tasks/*", "log/", "public/**/*", "script/**/*", "spec/**/*", "tmp/"]
|
16
|
+
p.development_dependencies = []
|
17
|
+
end
|
18
|
+
|
19
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'mir_utility'
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# == Configuration
|
2
|
+
#
|
3
|
+
# === Configure your application to default to the custom form builder
|
4
|
+
#
|
5
|
+
# In app/controllers/application_controller.rb:
|
6
|
+
#
|
7
|
+
# class ApplicationController < ActionController::Base
|
8
|
+
# ActionView::Base.default_form_builder = MirFormBuilder
|
9
|
+
#
|
10
|
+
# == Usage
|
11
|
+
#
|
12
|
+
# === Default form field with label:
|
13
|
+
#
|
14
|
+
# <%= f.text_field :last_name -%>
|
15
|
+
#
|
16
|
+
# Returns:
|
17
|
+
#
|
18
|
+
# <fieldset>
|
19
|
+
# <label for="user_last_name">Last Name</label><br />
|
20
|
+
# <input id="user_last_name" name="user[last_name]" size="30" type="text" />
|
21
|
+
# </fieldset>
|
22
|
+
#
|
23
|
+
# === Form field with custom label:
|
24
|
+
#
|
25
|
+
# <%= f.text_field :first_name, :label => 'Custom' -%>
|
26
|
+
#
|
27
|
+
# Returns:
|
28
|
+
#
|
29
|
+
# <fieldset>
|
30
|
+
# <label for="user_first_name">Custom</label><br />
|
31
|
+
# <input id="user_first_name" name="user[first_name]" size="30" type="text" />
|
32
|
+
# </fieldset>
|
33
|
+
#
|
34
|
+
# === Form field with no label
|
35
|
+
#
|
36
|
+
# <%= f.text_field :search, :label => false -%>
|
37
|
+
#
|
38
|
+
# Returns:
|
39
|
+
#
|
40
|
+
# <input id="search" name="search" size="30" type="text" />
|
41
|
+
#
|
42
|
+
# === Form field with inline help (? icon which reveals help content when clicked):
|
43
|
+
#
|
44
|
+
# <%= f.password_field :password %>
|
45
|
+
#
|
46
|
+
# Returns:
|
47
|
+
#
|
48
|
+
# <fieldset>
|
49
|
+
# <label for="password">Password: <img src="/images/icons/help_icon.png"
|
50
|
+
# onclick="$('password_help').toggle();" class='inline_icon' /></label><br />
|
51
|
+
# <div class="inline_help" id="password_help" style="display: none;">
|
52
|
+
# <p>Here are some detailed instructions on valid passwords.</p>
|
53
|
+
# </div>
|
54
|
+
# <input id="user_password" name="user[password]" size="30" type="password" />
|
55
|
+
# </fieldset>
|
56
|
+
#
|
57
|
+
# === Form field with instructions (show immediately below the label):
|
58
|
+
#
|
59
|
+
# <%= f.password_field :password_confirmation %>
|
60
|
+
#
|
61
|
+
# Returns:
|
62
|
+
#
|
63
|
+
# <fieldset>
|
64
|
+
# <label for="password_confirmation">
|
65
|
+
# Confirm Password:
|
66
|
+
# <span class="instructions">Enter your password again to confirm.</span>
|
67
|
+
# </label><br />
|
68
|
+
# <input id="user_password_confirmation" name="user[password_confirmation]" size="30" type="password" />
|
69
|
+
# </fieldset>
|
70
|
+
#
|
71
|
+
# === Check box with label in addition to checkbox value text
|
72
|
+
# (E.g. 'Foo' appears above the checkbox, and 'Something' next to it):
|
73
|
+
#
|
74
|
+
# <%= f.check_box :foo, :inline_label => 'Something' -%>
|
75
|
+
#
|
76
|
+
# Returns:
|
77
|
+
#
|
78
|
+
# <fieldset>
|
79
|
+
# <label for="user_foo">Foo</label><br />
|
80
|
+
# <input name="user[foo]" type="hidden" value="0" />
|
81
|
+
# <input id="user_foo" name="user[foo]" type="checkbox" value="1" />
|
82
|
+
# <label class="inline" for="user_foo">Something</label><br style='clear: both;'/><br />
|
83
|
+
# </fieldset>
|
84
|
+
#
|
85
|
+
# === Don't wrap fields in a fieldset
|
86
|
+
#
|
87
|
+
# <%= f.text_field :query, :label => 'Search terms:', :fieldset => false -%>
|
88
|
+
#
|
89
|
+
# Returns
|
90
|
+
#
|
91
|
+
# <label for="search_terms_query"><br />
|
92
|
+
# <input id="search_terms_query" name="search_terms_query" size="30" />
|
93
|
+
#
|
94
|
+
# == Troubleshooting
|
95
|
+
#
|
96
|
+
# If you're seeing double form labels, it's because you still have <%= label -%> elements in your forms.
|
97
|
+
#
|
98
|
+
class MirFormBuilder < ActionView::Helpers::FormBuilder
|
99
|
+
include ApplicationHelper
|
100
|
+
|
101
|
+
helpers = field_helpers +
|
102
|
+
%w{date_select datetime_select time_select} +
|
103
|
+
%w{collection_select select country_select time_zone_select} -
|
104
|
+
%w{hidden_field label fields_for}
|
105
|
+
|
106
|
+
helpers.each do |name|
|
107
|
+
define_method(name) do |field, *args|
|
108
|
+
RAILS_DEFAULT_LOGGER.debug "Processing #{name}"
|
109
|
+
RAILS_DEFAULT_LOGGER.debug " Arguments: #{args.inspect}"
|
110
|
+
|
111
|
+
# capture first array as select choices
|
112
|
+
choices = args.detect{ |a| a.is_a?(Array) } || []
|
113
|
+
args.delete(choices)
|
114
|
+
|
115
|
+
# capture first hash as options
|
116
|
+
options = args.detect{ |a| a.is_a?(Hash) } || {}
|
117
|
+
args.delete(options)
|
118
|
+
|
119
|
+
# capture second hash as HTML options
|
120
|
+
html_options = args.detect{ |a| a.is_a?(Hash) } || {}
|
121
|
+
args.delete(html_options)
|
122
|
+
|
123
|
+
include_label = true
|
124
|
+
|
125
|
+
RAILS_DEFAULT_LOGGER.debug " Choices: #{choices.inspect}"
|
126
|
+
RAILS_DEFAULT_LOGGER.debug " Options: #{options.inspect}"
|
127
|
+
RAILS_DEFAULT_LOGGER.debug " HTML options: #{html_options.inspect}"
|
128
|
+
RAILS_DEFAULT_LOGGER.debug " Remaining arguments: #{args.inspect}"
|
129
|
+
|
130
|
+
if options[:label].nil? # Not specified. Default to humanized version of field id.
|
131
|
+
_label_text = field.to_s.humanize.capitalize_words
|
132
|
+
elsif options[:label]
|
133
|
+
_label_text = options.delete :label
|
134
|
+
# options[:label] is false!
|
135
|
+
else
|
136
|
+
include_label = options.delete(:label)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Create label, if label text was provided or created.
|
140
|
+
if _label_text || options[:instructions]
|
141
|
+
if options[:instructions]
|
142
|
+
_label = tag_for_label_with_instructions(_label_text, field, options.delete(:instructions))
|
143
|
+
elsif options[:help]
|
144
|
+
_label = tag_for_label_with_inline_help(_label_text, field, options.delete(:help))
|
145
|
+
elsif include_label
|
146
|
+
_label = label(field, _label_text) + @template.tag('br')
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
if options[:inline_label] # Handle inline labels, e.g. for checkboxes
|
151
|
+
_inline_label = label(field, options.delete(:inline_label), :class => 'inline') + @template.tag('br', :style => 'clear: both;')
|
152
|
+
end
|
153
|
+
|
154
|
+
_field = nil
|
155
|
+
|
156
|
+
if name == 'select'
|
157
|
+
_field = super(field, choices, options, html_options)
|
158
|
+
elsif name.include? 'select'
|
159
|
+
_field = super(field, options, html_options)
|
160
|
+
else
|
161
|
+
if name == 'radio_button'
|
162
|
+
# invert arguments
|
163
|
+
_field = super(field, args, options)
|
164
|
+
else
|
165
|
+
_field = args.compact.blank? ? super(field, options) : super(field, options, args)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
if options[:fieldset] == false
|
170
|
+
"#{_label}#{_field}#{_inline_label}"
|
171
|
+
else
|
172
|
+
@template.content_tag(:fieldset, "#{_label}#{_field}#{_inline_label}")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
data/lib/mir_utility.rb
ADDED
@@ -0,0 +1,853 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'soap/header/simplehandler'
|
3
|
+
|
4
|
+
module MirUtility
|
5
|
+
MONTHS = {
|
6
|
+
0 => "JAN",
|
7
|
+
1 => "FEB",
|
8
|
+
2 => "MAR",
|
9
|
+
3 => "APR",
|
10
|
+
4 => "MAY",
|
11
|
+
5 => "JUN",
|
12
|
+
6 => "JUL",
|
13
|
+
7 => "AUG",
|
14
|
+
8 => "SEP",
|
15
|
+
9 => "OCT",
|
16
|
+
10 => "NOV",
|
17
|
+
11 => "DEC"
|
18
|
+
}
|
19
|
+
|
20
|
+
STATE_CODES = { 'Alabama' => 'AL', 'Alaska' => 'AK', 'Arizona' => 'AZ', 'Arkansas' => 'AR', 'California' => 'CA', 'Colorado' => 'CO', 'Connecticut' => 'CT', 'Delaware' => 'DE', 'Florida' => 'FL', 'Georgia' => 'GA', 'Hawaii' => 'HI', 'Idaho' => 'ID', 'Illinois' => 'IL', 'Indiana' => 'IN', 'Iowa' => 'IA', 'Kansas' => 'KS', 'Kentucky' => 'KY', 'Louisiana' => 'LA', 'Maine' => 'ME', 'Maryland' => 'MD', 'Massachusetts' => 'MA', 'Michigan' => 'MI', 'Minnesota' => 'MN', 'Mississippi' => 'MS', 'Missouri' => 'MO', 'Montana' => 'MT', 'Nebraska' => 'NE', 'Nevada' => 'NV', 'New Hampshire' => 'NH', 'New Jersey' => 'NJ', 'New Mexico' => 'NM', 'New York' => 'NY', 'North Carolina' => 'NC', 'North Dakota' => 'ND', 'Ohio' => 'OH', 'Oklahoma' => 'OK', 'Oregon' => 'OR', 'Pennsylvania' => 'PA', 'Puerto Rico' => 'PR', 'Rhode Island' => 'RI', 'South Carolina' => 'SC', 'South Dakota' => 'SD', 'Tennessee' => 'TN', 'Texas' => 'TX', 'Utah' => 'UT', 'Vermont' => 'VT', 'Virginia' => 'VA', 'Washington' => 'WA', 'Washington DC' => 'DC', 'West Virginia' => 'WV', 'Wisconsin' => 'WI', 'Wyoming' => 'WY', 'Alberta' => 'AB', 'British Columbia' => 'BC', 'Manitoba' => 'MB', 'New Brunswick' => 'NB', 'Newfoundland and Labrador' => 'NL', 'Northwest Territories' => 'NT', 'Nova Scotia' => 'NS', 'Nunavut' => 'NU', 'Ontario' => 'ON', 'Prince Edward Island' => 'PE', 'Quebec' => 'QC', 'Saskatchewan' => 'SK', 'Yukon' => 'YT' }
|
21
|
+
|
22
|
+
def self.canonical_url(url)
|
23
|
+
(url + '/').gsub(/\/\/$/,'/')
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.destroy_old_sessions( timestamp )
|
27
|
+
ActiveRecord::SessionStore::Session.destroy_all(
|
28
|
+
['updated_at < ?', timestamp]
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Copied from FriendlyId 2.2.7 for backward compatibility. Use gem's default behavior instead: http://github.com/norman/friendly_id.
|
33
|
+
def self.normalize(slug_text)
|
34
|
+
return "" if slug_text.nil? || slug_text == ""
|
35
|
+
ActiveSupport::Multibyte.proxy_class.new(slug_text.to_s).normalize(:kc).
|
36
|
+
gsub(/[\W]/u, ' ').
|
37
|
+
strip.
|
38
|
+
gsub(/\s+/u, '-').
|
39
|
+
gsub(/-\z/u, '').
|
40
|
+
downcase.
|
41
|
+
to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.normalize_slug(text)
|
45
|
+
warn "[DEPRECATION] `MirUtility.normalize_slug` is deprecated. Use FriendlyId's default behavior instead: http://github.com/norman/friendly_id."
|
46
|
+
_normalized = MirUtility.normalize(text)
|
47
|
+
_normalized.gsub!('-', '_') # change - to _
|
48
|
+
_normalized.gsub!(/[_]+/, '_') # truncate multiple _
|
49
|
+
_normalized
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.state_name_for(abbreviation)
|
53
|
+
STATE_CODES.value?(abbreviation.to_s.upcase) && STATE_CODES.find{|k,v| v == abbreviation.to_s.upcase}[0]
|
54
|
+
end
|
55
|
+
|
56
|
+
module CoreExtensions
|
57
|
+
module String
|
58
|
+
# Add whatever helpers you want, then wrap any methods that you want from the
|
59
|
+
# ActionView::Helpers::Foo class
|
60
|
+
module NumberHelper
|
61
|
+
def number_to_phone(s, options = {})
|
62
|
+
StringHelperSingleton.instance.number_to_phone(s, options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
class ActionController::Base
|
71
|
+
include MirUtility
|
72
|
+
require 'socket'
|
73
|
+
|
74
|
+
def self.local_ip
|
75
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
76
|
+
|
77
|
+
UDPSocket.open do |s|
|
78
|
+
s.connect '64.233.187.99', 1
|
79
|
+
s.addr.last
|
80
|
+
end
|
81
|
+
ensure
|
82
|
+
Socket.do_not_reverse_lookup = orig
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns a sanitized column parameter suitable for SQL order-by clauses.
|
86
|
+
def sanitize_by_param(allowed=[], default='id')
|
87
|
+
sanitize_params params && params[:by], allowed, default
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns a sanitized direction parameter suitable for SQL order-by clauses.
|
91
|
+
def sanitize_dir_param(default='ASC')
|
92
|
+
sanitize_params params && params[:dir], ['ASC', 'DESC'], default
|
93
|
+
end
|
94
|
+
|
95
|
+
# Use this method to prevent SQL injection vulnerabilities by verifying that a user-provided
|
96
|
+
# parameter is on a whitelist of allowed values.
|
97
|
+
#
|
98
|
+
# Accepts a value, a list of allowed values, and a default value.
|
99
|
+
# Returns the value if allowed, otherwise the default.
|
100
|
+
def sanitize_params(supplied='', allowed=[], default=nil)
|
101
|
+
raise ArgumentError, "A default value is required." unless default
|
102
|
+
return default.to_s if supplied.blank? || allowed.blank? || ! allowed.map{ |x| x.to_s }.include?(supplied.to_s)
|
103
|
+
return supplied
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module ActiveRecord::Validations::ClassMethods
|
108
|
+
|
109
|
+
# Overrides validates associates.
|
110
|
+
# We do this to allow more better error messages to bubble up from associated models.
|
111
|
+
# Adapted from thread at http://pivotallabs.com/users/nick/blog/articles/359-alias-method-chain-validates-associated-informative-error-message
|
112
|
+
def validates_associated(*associations)
|
113
|
+
# These configuration lines are required if your going to use any conditionals with the validates associated - Rails 2.2.2 safe!
|
114
|
+
configuration = { :message => I18n.translate('activerecord.errors.messages'), :on => :save }
|
115
|
+
configuration.update(associations.extract_options!)
|
116
|
+
associations.each do |association|
|
117
|
+
class_eval do
|
118
|
+
validates_each(associations,configuration) do |record, associate_name, value|
|
119
|
+
associates = record.send(associate_name)
|
120
|
+
associates = [associates] unless associates.respond_to?('each')
|
121
|
+
associates.each{ |associate| associate.errors.each{ |key, value2| record.errors.add("", "#{value2}") } if associate && !associate.valid? }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class ActiveRecord::Base
|
129
|
+
include MirUtility
|
130
|
+
|
131
|
+
#FIXME Extending AR in this way will stop working under Rails 2.3.2 for some reason.
|
132
|
+
|
133
|
+
named_scope :order_by, lambda{ |col, dir| {:order => (col.blank?) ? ( (dir.blank?) ? 'id' : dir ) : "#{col} #{dir}"} }
|
134
|
+
named_scope :limit, lambda { |num| { :limit => num } }
|
135
|
+
|
136
|
+
# TODO: call the column_names class method on the subclass
|
137
|
+
# named_scope :sort_by, lambda{ |col, dir| {:order => (col.blank?) ? ( (dir.blank?) ? (Client.column_names.include?('name') ? 'name' : 'id') : h(dir) ) : "#{h(col)} #{h(dir)}"} }
|
138
|
+
|
139
|
+
# Returns an array of SQL conditions suitable for use with ActiveRecord's finder.
|
140
|
+
# valid_criteria is an array of valid search fields.
|
141
|
+
# pairs is a hash of field names and values.
|
142
|
+
def self.search_conditions( valid_search_criteria, pairs, operator = 'OR' )
|
143
|
+
if valid_search_criteria.detect{ |_c| ! pairs[_c].blank? } || ! pairs[:query].blank?
|
144
|
+
_conditions = []
|
145
|
+
_or_clause = ''
|
146
|
+
_or_clause_values = []
|
147
|
+
_int_terms = {}
|
148
|
+
_text_terms = {}
|
149
|
+
|
150
|
+
# build or clause for keyword search
|
151
|
+
unless pairs[:query].blank? || ! self.respond_to?(:flattened_content)
|
152
|
+
pairs[:query].split(' ').each do |keyword|
|
153
|
+
_or_clause += 'flattened_content LIKE ? OR '
|
154
|
+
_or_clause_values << "%#{keyword}%"
|
155
|
+
end
|
156
|
+
|
157
|
+
_or_clause.gsub!( / OR $/, '')
|
158
|
+
end
|
159
|
+
|
160
|
+
# iterate across each valid search field
|
161
|
+
valid_search_criteria.each do |_field|
|
162
|
+
# build or clause for keyword search
|
163
|
+
unless pairs[:query].blank? || self.respond_to?(:flattened_content)
|
164
|
+
pairs[:query].split(' ').each do |keyword|
|
165
|
+
_or_clause += "#{_field} LIKE ? OR "
|
166
|
+
_or_clause_values << "%#{keyword}%"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# build hashes of integer and/or text search fields and values for each non-blank param
|
171
|
+
if ! pairs[_field].blank?
|
172
|
+
_field.to_s =~ /^id$|_id$|\?$/ ? _int_terms[_field.to_s.gsub('?', '')] = pairs[_field] : _text_terms[_field] = pairs[_field]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
_or_clause.gsub!( / OR $/, '')
|
177
|
+
|
178
|
+
# convert the hash to parametric SQL
|
179
|
+
if _or_clause.blank?
|
180
|
+
_conditions = sql_conditions_for( _int_terms, _text_terms, nil, operator )
|
181
|
+
elsif _int_terms.keys.empty? && _text_terms.keys.empty?
|
182
|
+
_conditions = [ _or_clause ]
|
183
|
+
else
|
184
|
+
_conditions = sql_conditions_for( _int_terms, _text_terms, _or_clause, operator )
|
185
|
+
end
|
186
|
+
|
187
|
+
# add integer values
|
188
|
+
_int_terms.keys.each{ |key| _conditions << _int_terms[key] }
|
189
|
+
# add wildcard-padded values
|
190
|
+
_text_terms.keys.each{ |key| _conditions << "%#{_text_terms[key]}%" }
|
191
|
+
|
192
|
+
unless _or_clause_values.empty?
|
193
|
+
# add keywords
|
194
|
+
_conditions += _or_clause_values
|
195
|
+
end
|
196
|
+
|
197
|
+
return _conditions
|
198
|
+
else
|
199
|
+
return nil
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def self.to_option_values
|
204
|
+
self.all.sort_by{ |x| x.name }.map{ |x| [x.name, x.id] }
|
205
|
+
end
|
206
|
+
|
207
|
+
# Strips the specified attribute's value.
|
208
|
+
def strip(attribute)
|
209
|
+
value = self[attribute]
|
210
|
+
self.send("#{attribute}=", value && value.strip)
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
def self.sql_conditions_for( integer_fields, text_fields, or_clause = nil, operator = 'OR' )
|
216
|
+
if integer_fields.empty? && ! text_fields.empty?
|
217
|
+
[ text_fields.keys.map{ |k| k } * " LIKE ? #{operator} " + ' LIKE ?' + (or_clause ? " #{operator} #{or_clause}" : '') ]
|
218
|
+
elsif ! integer_fields.empty? && text_fields.empty?
|
219
|
+
[ integer_fields.keys.map{ |k| k } * " = ? #{operator} " + ' = ?' + (or_clause ? " #{operator} #{or_clause}" : '') ]
|
220
|
+
else
|
221
|
+
[ integer_fields.keys.map{ |k| k } * " = ? #{operator} " + " = ? #{operator} " + text_fields.keys.map{ |k| k } * " LIKE ? #{operator} " + ' LIKE ?' + (or_clause ? " #{operator} #{or_clause}" : '') ]
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
module ApplicationHelper
|
227
|
+
|
228
|
+
SELECT_PROMPT = 'Select...'
|
229
|
+
SELECT_PROMPT_OPTION = "<option value=''>#{SELECT_PROMPT}</option>"
|
230
|
+
|
231
|
+
def action?( expression )
|
232
|
+
!! ( expression.class == Regexp ? controller.action_name =~ expression : controller.action_name == expression )
|
233
|
+
end
|
234
|
+
|
235
|
+
# Formats an array with HTML line breaks, or the specified delimiter.
|
236
|
+
def array_to_lines(array, delimiter = '<br />')
|
237
|
+
return unless array.is_a?(Array)
|
238
|
+
array.blank? ? nil : array * delimiter
|
239
|
+
end
|
240
|
+
|
241
|
+
def checkmark
|
242
|
+
%{<div class="checkmark"></div>}
|
243
|
+
end
|
244
|
+
|
245
|
+
def controller?( expression )
|
246
|
+
!! ( expression.class == Regexp ? controller.controller_name =~ expression : controller.controller_name == expression )
|
247
|
+
end
|
248
|
+
|
249
|
+
# Display CRUD icons or links, according to setting in use_crud_icons method.
|
250
|
+
#
|
251
|
+
# In application_helper.rb:
|
252
|
+
#
|
253
|
+
# def use_crud_icons
|
254
|
+
# true
|
255
|
+
# end
|
256
|
+
#
|
257
|
+
# Then use in index views like this:
|
258
|
+
#
|
259
|
+
# <td class="crud_links"><%= crud_links(my_model, 'my_model', [:show, :edit, :delete]) -%></td>
|
260
|
+
#
|
261
|
+
def crud_links(model, instance_name, actions, args={})
|
262
|
+
_html = ''
|
263
|
+
_path = args.delete(:path) || model
|
264
|
+
_edit_path = args.delete(:edit_path) || eval("edit_#{instance_name}_path(model)") if actions.include?(:edit)
|
265
|
+
_options = args.empty? ? '' : ", #{args.map{|k,v| ":#{k} => #{v}"}}"
|
266
|
+
|
267
|
+
if use_crud_icons
|
268
|
+
_html << link_to(image_tag('icons/view.png', :class => 'crud_icon', :width => 14, :height => 14), _path, :title => "View#{_options}") if actions.include?(:show)
|
269
|
+
_html << link_to(image_tag('icons/edit.png', :class => 'crud_icon', :width => 14, :height => 14), _edit_path, :title => "Edit#{_options}") if actions.include?(:edit)
|
270
|
+
_html << link_to(image_tag('icons/delete.png', :class => 'crud_icon', :width => 14, :height => 14), _path, :confirm => 'Are you sure? This action cannot be undone.', :method => :delete, :title => "Delete#{_options}") if actions.include?(:delete)
|
271
|
+
else
|
272
|
+
_html << link_to('View', _path, :title => 'View', :class => "crud_link#{_options}") if actions.include?(:show)
|
273
|
+
_html << link_to('Edit', _edit_path, :title => 'Edit', :class => "crud_link#{_options}") if actions.include?(:edit)
|
274
|
+
_html << link_to('Delete', _path, :confirm => 'Are you sure? This action cannot be undone.', :method => :delete, :title => 'Delete', :class => "crud_link#{_options}") if actions.include?(:delete)
|
275
|
+
end
|
276
|
+
|
277
|
+
_html
|
278
|
+
end
|
279
|
+
|
280
|
+
# Display CRUD icons or links, according to setting in use_crud_icons method.
|
281
|
+
# This method works with nested resources.
|
282
|
+
# Use in index views like this:
|
283
|
+
#
|
284
|
+
# <td class="crud_links"><%= crud_links_for_nested_resource(@my_model, my_nested_model, 'my_model', 'my_nested_model', [:show, :edit, :delete]) -%></td>
|
285
|
+
#
|
286
|
+
def crud_links_for_nested_resource(model, nested_model, model_instance_name, nested_model_instance_name, actions, args={})
|
287
|
+
crud_links model, model_instance_name, actions, args.merge(:edit_path => eval("edit_#{model_instance_name}_#{nested_model_instance_name}_path(model, nested_model)"), :path => [model, nested_model])
|
288
|
+
end
|
289
|
+
|
290
|
+
# DRY way to return a legend tag that renders correctly in all browsers. This variation allows
|
291
|
+
# for more "stuff" inside the legend tag, e.g. expand/collapse controls, without having to worry
|
292
|
+
# about escape sequences.
|
293
|
+
#
|
294
|
+
# Sample usage:
|
295
|
+
#
|
296
|
+
# <%- legend_block do -%>
|
297
|
+
# <span id="hide_or_show_backlinks" class="show_link" style="background-color: #999999;
|
298
|
+
# border: 1px solid #999999;" onclick="javascript:hide_or_show('backlinks');"></span>Backlinks (<%=
|
299
|
+
# @google_results.size -%>)
|
300
|
+
# <%- end -%>
|
301
|
+
#
|
302
|
+
def legend_block(&block)
|
303
|
+
concat content_tag(:div, capture(&block), :class => "faux_legend")
|
304
|
+
end
|
305
|
+
|
306
|
+
# DRY way to return a legend tag that renders correctly in all browsers
|
307
|
+
#
|
308
|
+
# Sample usage:
|
309
|
+
#
|
310
|
+
# <%= legend_tag "Report Criteria" -%>
|
311
|
+
#
|
312
|
+
# Sample usage with help text:
|
313
|
+
#
|
314
|
+
# <%= legend_tag "Report Criteria", :help => "Some descriptive copy here." -%>
|
315
|
+
# <span id="hide_or_show_backlinks" class="show_link" style="background-color: #999999;
|
316
|
+
# border: 1px solid #999999;" onclick="javascript:hide_or_show('backlinks');"></span>Backlinks (<%=
|
317
|
+
# @google_results.size -%>)
|
318
|
+
# <%- end -%>
|
319
|
+
#
|
320
|
+
# Recommended CSS to support display of help icon and text:
|
321
|
+
#
|
322
|
+
# .help_icon {
|
323
|
+
# display: block;
|
324
|
+
# float: left;
|
325
|
+
# margin-top: -16px;
|
326
|
+
# margin-left: 290px;
|
327
|
+
# border-left: 1px solid #444444;
|
328
|
+
# padding: 3px 6px;
|
329
|
+
# cursor: pointer;
|
330
|
+
# }
|
331
|
+
#
|
332
|
+
# div.popup_help {
|
333
|
+
# color: #666666;
|
334
|
+
# width: 98%;
|
335
|
+
# background: #ffffff;
|
336
|
+
# padding: 1em;
|
337
|
+
# border: 1px solid #999999;
|
338
|
+
# margin: 1em 0em;
|
339
|
+
# }
|
340
|
+
#
|
341
|
+
def legend_tag(text, args={})
|
342
|
+
args[:id] ||= text.downcase.gsub(/ /,'_')
|
343
|
+
if args[:help]
|
344
|
+
_html = %{<div id="#{args[:id]}" class="faux_legend">#{text}<span class="help_icon" onclick="$('#{args[:id]}_help').toggle();">?</span></div>\r}
|
345
|
+
_html << %{<div id="#{args[:id]}_help" class="popup_help" style="display: none;">#{args[:help]}<br /></div>\r}
|
346
|
+
else
|
347
|
+
_html = %{<div id="#{args[:id]}" class="faux_legend">#{text}</div>\r}
|
348
|
+
end
|
349
|
+
_html.gsub!(/ id=""/,'')
|
350
|
+
_html.gsub!(/ class=""/,'')
|
351
|
+
_html
|
352
|
+
end
|
353
|
+
|
354
|
+
def meta_description(content=nil)
|
355
|
+
content_for(:meta_description) { content } unless content.blank?
|
356
|
+
end
|
357
|
+
|
358
|
+
def meta_keywords(content=nil)
|
359
|
+
content_for(:meta_keywords) { content } unless content.blank?
|
360
|
+
end
|
361
|
+
|
362
|
+
def models_for_select( models, label = 'name' )
|
363
|
+
models.map{ |m| [m[label], m.id] }.sort_by{ |e| e[0] }
|
364
|
+
end
|
365
|
+
|
366
|
+
def options_for_array( a, selected = nil, prompt = SELECT_PROMPT )
|
367
|
+
"<option value=''>#{prompt}</option>" + a.map{ |_e| _flag = _e[0].to_s == selected ? 'selected="1"' : ''; _e.is_a?(Array) ? "<option value=\"#{_e[0]}\" #{_flag}>#{_e[1]}</option>" : "<option>#{_e}</option>" }.to_s
|
368
|
+
end
|
369
|
+
|
370
|
+
# Create a link that is opaque to search engine spiders.
|
371
|
+
def obfuscated_link_to(path, image, label, args={})
|
372
|
+
_html = %{<form action="#{path}" method="get" class="obfuscated_link">}
|
373
|
+
_html << %{ <fieldset><input alt="#{label}" src="#{image}" type="image" /></fieldset>}
|
374
|
+
args.each{ |k,v| _html << %{ <div><input id="#{k.to_s}" name="#{k}" type="hidden" value="#{v}" /></div>} }
|
375
|
+
_html << %{</form>}
|
376
|
+
_html
|
377
|
+
end
|
378
|
+
|
379
|
+
# Wraps the given HTML in Rails' default style to highlight validation errors, if any.
|
380
|
+
def required_field_helper( model, element, html )
|
381
|
+
if model && ! model.errors.empty? && element.is_required
|
382
|
+
return content_tag( :div, html, :class => 'fieldWithErrors' )
|
383
|
+
else
|
384
|
+
return html
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
# Use on index pages to create dropdown list of filtering criteria.
|
389
|
+
# Populate the filter list using a constant in the model corresponding to named scopes.
|
390
|
+
#
|
391
|
+
# Usage:
|
392
|
+
#
|
393
|
+
# - item.rb:
|
394
|
+
#
|
395
|
+
# named_scope :active, :conditions => { :is_active => true }
|
396
|
+
# named_scope :inactive, :conditions => { :is_active => false }
|
397
|
+
#
|
398
|
+
# FILTERS = [
|
399
|
+
# {:scope => "all", :label => "All"},
|
400
|
+
# {:scope => "active", :label => "Active Only"},
|
401
|
+
# {:scope => "inactive", :label => "Inactive Only"}
|
402
|
+
# ]
|
403
|
+
#
|
404
|
+
# - items/index.html.erb:
|
405
|
+
#
|
406
|
+
# <%= select_tag_for_filter("items", @filters, params) -%>
|
407
|
+
#
|
408
|
+
# - items_controller.rb:
|
409
|
+
#
|
410
|
+
# def index
|
411
|
+
# @filters = Item::FILTERS
|
412
|
+
# if params[:show] && params[:show] != "all" && @filters.collect{|f| f[:scope]}.include?(params[:show])
|
413
|
+
# @items = eval("@items.#{params[:show]}.order_by(params[:by], params[:dir])")
|
414
|
+
# else
|
415
|
+
# @items = @items.order_by(params[:by], params[:dir])
|
416
|
+
# end
|
417
|
+
# ...
|
418
|
+
# end
|
419
|
+
#
|
420
|
+
def select_tag_for_filter(model, filters, params)
|
421
|
+
return unless model && ! filters.blank?
|
422
|
+
|
423
|
+
# capture and delete previous show param
|
424
|
+
_old_show = params.delete :show
|
425
|
+
|
426
|
+
_html = %{Show <select name="show" id="show" onchange="window.location='#{eval("#{model}_url")}?#{params.to_params}&show=' + this.value">}
|
427
|
+
|
428
|
+
# restore previous show param
|
429
|
+
params[:show] = _old_show
|
430
|
+
|
431
|
+
filters.each do |pair|
|
432
|
+
_html = %{#{_html}<option value="#{pair[:scope]}"}
|
433
|
+
_html = %{#{_html} selected="selected"} if params[:show] == pair[:scope]
|
434
|
+
_html = %{#{_html}>#{pair[:label]}</option>}
|
435
|
+
end
|
436
|
+
|
437
|
+
_html = %{#{_html}</select>}
|
438
|
+
end
|
439
|
+
|
440
|
+
# Returns a link_to tag with sorting parameters that can be used with ActiveRecord.order_by.
|
441
|
+
#
|
442
|
+
# To use standard resources, specify the resources as a plural symbol:
|
443
|
+
# sort_link(:users, 'email', params)
|
444
|
+
#
|
445
|
+
# To use resources aliased with :as (in routes.rb), specify the aliased route as a string.
|
446
|
+
# sort_link('users_admin', 'email', params)
|
447
|
+
#
|
448
|
+
# You can override the link's label by adding a labels hash to your params in the controller:
|
449
|
+
# params[:labels] = {'user_id' => 'User'}
|
450
|
+
def sort_link(model, field, params, html_options={})
|
451
|
+
if (field.to_sym == params[:by] || field == params[:by]) && params[:dir] == "ASC"
|
452
|
+
classname = "arrow-asc"
|
453
|
+
dir = "DESC"
|
454
|
+
elsif (field.to_sym == params[:by] || field == params[:by])
|
455
|
+
classname = "arrow-desc"
|
456
|
+
dir = "ASC"
|
457
|
+
else
|
458
|
+
dir = "ASC"
|
459
|
+
end
|
460
|
+
|
461
|
+
options = {
|
462
|
+
:anchor => html_options[:anchor],
|
463
|
+
:by => field,
|
464
|
+
:dir => dir,
|
465
|
+
:query => params[:query],
|
466
|
+
:show => params[:show]
|
467
|
+
}
|
468
|
+
|
469
|
+
options[:show] = params[:show] unless params[:show].blank? || params[:show] == 'all'
|
470
|
+
|
471
|
+
html_options = {
|
472
|
+
:class => "#{classname} #{html_options[:class]}",
|
473
|
+
:style => "color: white; font-weight: #{params[:by] == field ? "bold" : "normal"}; #{html_options[:style]}",
|
474
|
+
:title => "Sort by this field"
|
475
|
+
}
|
476
|
+
|
477
|
+
field_name = params[:labels] && params[:labels][field] ? params[:labels][field] : field.titleize
|
478
|
+
|
479
|
+
_link = model.is_a?(Symbol) ? eval("#{model}_url(options)") : "/#{model}?#{options.to_params}"
|
480
|
+
link_to(field_name, _link, html_options)
|
481
|
+
end
|
482
|
+
|
483
|
+
# Tabbed interface helpers =======================================================================
|
484
|
+
|
485
|
+
# Returns formatted tabs with appropriate JS for activation. Use in conjunction with tab_body.
|
486
|
+
#
|
487
|
+
# Usage:
|
488
|
+
#
|
489
|
+
# <%- tabset do -%>
|
490
|
+
# <%= tab_tag :id => 'ppc_ads', :label => 'PPC Ads', :state => 'active' %>
|
491
|
+
# <%= tab_tag :id => 'budget' %>
|
492
|
+
# <%= tab_tag :id => 'geotargeting' %>
|
493
|
+
# <%- end -%>
|
494
|
+
#
|
495
|
+
def tabset(&proc)
|
496
|
+
concat %{
|
497
|
+
<div class="jump_links">
|
498
|
+
<ul>
|
499
|
+
}
|
500
|
+
yield
|
501
|
+
concat %{
|
502
|
+
</ul>
|
503
|
+
</div>
|
504
|
+
<br style="clear: both;" /><br />
|
505
|
+
<input type="hidden" id="show_tab" />
|
506
|
+
<script type="text/javascript">
|
507
|
+
function hide_all_tabs() { $$('.tab_block').invoke('hide'); }
|
508
|
+
function activate_tab(tab) {
|
509
|
+
$$('.tab_control').each(function(elem){ elem.className = 'tab_control'});
|
510
|
+
$('show_' + tab).className = 'tab_control active';
|
511
|
+
hide_all_tabs();
|
512
|
+
$(tab).toggle();
|
513
|
+
$('show_tab').value = tab
|
514
|
+
}
|
515
|
+
function sticky_tab() { if (location.hash) { activate_tab(location.hash.gsub('#','')); } }
|
516
|
+
Event.observe(window, 'load', function() { sticky_tab(); });
|
517
|
+
</script>
|
518
|
+
}
|
519
|
+
end
|
520
|
+
|
521
|
+
# Returns a tab body corresponding to tabs in a tabset. Make sure that the id of the tab_body
|
522
|
+
# matches the id provided to the tab_tag in the tabset block.
|
523
|
+
#
|
524
|
+
# Usage:
|
525
|
+
#
|
526
|
+
# <%- tab_body :id => 'ppc_ads', :label => 'PPC Ad Details' do -%>
|
527
|
+
# PPC ads form here.
|
528
|
+
# <%- end -%>
|
529
|
+
#
|
530
|
+
# <%- tab_body :id => 'budget' do -%>
|
531
|
+
# Budget form here.
|
532
|
+
# <%- end -%>
|
533
|
+
#
|
534
|
+
# <%- tab_body :id => 'geotargeting' do -%>
|
535
|
+
# Geotargeting form here.
|
536
|
+
# <%- end -%>
|
537
|
+
#
|
538
|
+
def tab_body(args, &proc)
|
539
|
+
concat %{<div id="#{args[:id]}" class="tab_block form_container" style="display: #{args[:display] || 'none'};">}
|
540
|
+
concat %{#{legend_tag args[:label] || args[:id].titleize }}
|
541
|
+
concat %{<a name="#{args[:id]}"></a><br />}
|
542
|
+
yield
|
543
|
+
concat %{</div>}
|
544
|
+
end
|
545
|
+
|
546
|
+
# Returns the necessary HTML for a particular tab. Use inside a tabset block.
|
547
|
+
# Override the default tab label by specifying a :label parameter.
|
548
|
+
# Indicate that the tab should be active by setting its :state to 'active'.
|
549
|
+
# (NOTE: You must define a corresponding CSS style for active tabs.)
|
550
|
+
#
|
551
|
+
# Usage:
|
552
|
+
#
|
553
|
+
# <%= tab_tag :id => 'ppc_ads', :label => 'PPC Ads', :state => 'active' %>
|
554
|
+
#
|
555
|
+
def tab_tag(args, *css_class)
|
556
|
+
%{<li id="show_#{args[:id]}" class="tab_control #{args[:state]}" onclick="window.location='##{args[:id]}'; activate_tab('#{args[:id]}');">#{args[:label] || args[:id].to_s.titleize}</li>}
|
557
|
+
end
|
558
|
+
|
559
|
+
# ================================================================================================
|
560
|
+
|
561
|
+
def tag_for_collapsible_row(obj, params)
|
562
|
+
_html = ""
|
563
|
+
if obj && obj.respond_to?(:parent) && obj.parent
|
564
|
+
_html << %{<tr class="#{obj.class.name.downcase}_#{obj.parent.id} #{params[:class]}" style="display: none; #{params[:style]}">}
|
565
|
+
else
|
566
|
+
_html << %{<tr class="#{params[:class]}" style="#{params[:style]}">}
|
567
|
+
end
|
568
|
+
_html
|
569
|
+
end
|
570
|
+
|
571
|
+
def tag_for_collapsible_row_control(obj)
|
572
|
+
_base_id = "#{obj.class.name.downcase}_#{obj.id}"
|
573
|
+
_html = %{<div id="hide_or_show_#{_base_id}" class="show_link" style="background-color: #999999; border: 1px solid #999999;" onclick="javascript:hide_or_show('#{_base_id}');"></div>}
|
574
|
+
end
|
575
|
+
|
576
|
+
# Create a set of tags for displaying a field label with inline help.
|
577
|
+
# Field label text is appended with a ? icon, which responds to a click
|
578
|
+
# by showing or hiding the provided help text.
|
579
|
+
#
|
580
|
+
# Sample usage:
|
581
|
+
#
|
582
|
+
# <%= tag_for_label_with_inline_help 'Relative Frequency', 'rel_frequency', 'Relative frequency of search traffic for this keyword across multiple search engines, as measured by WordTracker.' %>
|
583
|
+
#
|
584
|
+
# Yields:
|
585
|
+
#
|
586
|
+
# <label for="rel_frequency">Relative Frequency: <%= image_tag "/images/help_icon.png", :onclick => "$('rel_frequency_help').toggle();", :class => 'inline_icon' %></label><br />
|
587
|
+
# <div class="inline_help" id="rel_frequency_help" style="display: none;">
|
588
|
+
# <p>Relative frequency of search traffic for this keyword across multiple search engines, as measured by WordTracker.</p>
|
589
|
+
# </div>
|
590
|
+
def tag_for_label_with_inline_help( label_text, field_id, help_text )
|
591
|
+
_html = ""
|
592
|
+
_html << %{<label for="#{field_id}">#{label_text}}
|
593
|
+
_html << %{<img src="/images/icons/help_icon.png" onclick="$('#{field_id}_help').toggle();" class='inline_icon' />}
|
594
|
+
_html << %{</label><br />}
|
595
|
+
_html << %{<div class="inline_help" id="#{field_id}_help" style="display: none;">}
|
596
|
+
_html << %{<p>#{help_text}</p>}
|
597
|
+
_html << %{</div>}
|
598
|
+
_html
|
599
|
+
end
|
600
|
+
|
601
|
+
# Create a set of tags for displaying a field label followed by instructions.
|
602
|
+
# The instructions are displayed on a new line following the field label.
|
603
|
+
#
|
604
|
+
# Usage:
|
605
|
+
#
|
606
|
+
# <%= tag_for_label_with_instructions 'Status', 'is_active', 'Only active widgets will be visible to the public.' %>
|
607
|
+
#
|
608
|
+
# Yields:
|
609
|
+
#
|
610
|
+
# <label for="is_active">
|
611
|
+
# Status<br />
|
612
|
+
# <span class="instructions">Only active widgets will be visible to the public.</span>
|
613
|
+
# <label><br />
|
614
|
+
def tag_for_label_with_instructions( label_text, field_id, instructions )
|
615
|
+
_html = ""
|
616
|
+
_html << %{<label for="#{field_id}">#{label_text}}
|
617
|
+
_html << %{<span class="instructions">#{instructions}</span>}
|
618
|
+
_html << %{</label><br />}
|
619
|
+
_html
|
620
|
+
end
|
621
|
+
|
622
|
+
end
|
623
|
+
|
624
|
+
class Array
|
625
|
+
def mean
|
626
|
+
self.inject(0){ |sum, x| sum += x } / self.size.to_f
|
627
|
+
end
|
628
|
+
|
629
|
+
def count
|
630
|
+
self.size
|
631
|
+
end
|
632
|
+
|
633
|
+
end
|
634
|
+
|
635
|
+
module Enumerable
|
636
|
+
def to_histogram
|
637
|
+
inject(Hash.new(0)) { |h,x| h[x] += 1; h }
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
class Fixnum
|
642
|
+
include MirUtility
|
643
|
+
|
644
|
+
# Given a number of seconds, convert into a string like HH:MM:SS
|
645
|
+
def to_hrs_mins_secs
|
646
|
+
_now = DateTime.now
|
647
|
+
_d = Date::day_fraction_to_time((_now + self.seconds) - _now)
|
648
|
+
"#{sprintf('%02d',_d[0])}:#{sprintf('%02d',_d[1])}:#{sprintf('%02d',_d[2])}"
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
class Float
|
653
|
+
include MirUtility
|
654
|
+
def to_nearest_tenth
|
655
|
+
sprintf("%.1f", self).to_f
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
class Hash
|
660
|
+
def to_params
|
661
|
+
params = ''
|
662
|
+
stack = []
|
663
|
+
|
664
|
+
each do |k, v|
|
665
|
+
if v.is_a?(Hash)
|
666
|
+
stack << [k,v]
|
667
|
+
elsif v.is_a?(Array)
|
668
|
+
stack << [k,Hash.from_array(v)]
|
669
|
+
else
|
670
|
+
params << "#{k}=#{v}&"
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
stack.each do |parent, hash|
|
675
|
+
hash.each do |k, v|
|
676
|
+
if v.is_a?(Hash)
|
677
|
+
stack << ["#{parent}[#{k}]", v]
|
678
|
+
else
|
679
|
+
params << "#{parent}[#{k}]=#{v}&"
|
680
|
+
end
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
params.chop!
|
685
|
+
params
|
686
|
+
end
|
687
|
+
|
688
|
+
def to_sql( operator = 'AND' )
|
689
|
+
_sql = self.keys.map do |_key|
|
690
|
+
_value = self[_key].is_a?(Fixnum) ? self[_key] : "'#{self[_key]}'"
|
691
|
+
self[_key].nil? ? '1 = 1' : "#{_key} = #{_value}"
|
692
|
+
end
|
693
|
+
_sql * " #{operator} "
|
694
|
+
end
|
695
|
+
|
696
|
+
def self.from_array(array = [])
|
697
|
+
h = Hash.new
|
698
|
+
array.size.times{ |t| h[t] = array[t] }
|
699
|
+
h
|
700
|
+
end
|
701
|
+
|
702
|
+
end
|
703
|
+
|
704
|
+
# Helper class for SOAP headers.
|
705
|
+
class Header < SOAP::Header::SimpleHandler
|
706
|
+
def initialize(tag, value)
|
707
|
+
super(XSD::QName.new(nil, tag))
|
708
|
+
@tag = tag
|
709
|
+
@value = value
|
710
|
+
end
|
711
|
+
|
712
|
+
def on_simple_outbound
|
713
|
+
@value
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
717
|
+
class String
|
718
|
+
|
719
|
+
# Bring in support for view helpers
|
720
|
+
include MirUtility::CoreExtensions::String::NumberHelper
|
721
|
+
|
722
|
+
# General methods
|
723
|
+
|
724
|
+
def capitalize_words
|
725
|
+
self.downcase.gsub(/\b([a-z])/) { $1.capitalize }.gsub( "'S", "'s" )
|
726
|
+
end
|
727
|
+
|
728
|
+
# Address methods
|
729
|
+
|
730
|
+
def expand_address_abbreviations
|
731
|
+
_address = self.strip.capitalize_words
|
732
|
+
|
733
|
+
# NOTE: DO NOT rearrange the replace sequences; order matters!
|
734
|
+
|
735
|
+
# streets
|
736
|
+
_address.gsub!( /\b(ave|av)\.?\b/i, 'Avenue ' )
|
737
|
+
_address.gsub!( /\b(blvd|blv|bld|bl)\.?\b/i, 'Boulevard ' )
|
738
|
+
_address.gsub!( /\bcr\.?\b/i, 'Circle ' )
|
739
|
+
_address.gsub!( /\bctr\.?\b/i, 'Center ' )
|
740
|
+
_address.gsub!( /\b(crt|ct)\.?\b/i, 'Court ' )
|
741
|
+
_address.gsub!( /\bdr\.?\b/i, 'Drive ' )
|
742
|
+
_address.gsub!( /\b(expressw|expw|expy)\.?\b/i, 'Expressway ' )
|
743
|
+
_address.gsub!( /\bfrwy\.?\b/i, 'Freeway ' )
|
744
|
+
_address.gsub!( /\bhwy\.?\b/i, 'Highway ' )
|
745
|
+
_address.gsub!( /\bln\.?\b/i, 'Lane ' )
|
746
|
+
_address.gsub!( /\b(prkwy|pkwy|pkw|pky)\.?\b/i, 'Parkway ' )
|
747
|
+
_address.gsub!( /\bpk\.?\b/i, 'Pike ' )
|
748
|
+
_address.gsub!( /\bplz\.?\b/i, 'Plaza ' )
|
749
|
+
_address.gsub!( /\bpl\.?\b/i, 'Place ' )
|
750
|
+
_address.gsub!( /\brd\.?\b/i, 'Road ' )
|
751
|
+
_address.gsub!( /\b(rte|rt)\.?\b/i, 'Route ' )
|
752
|
+
_address.gsub!( /\bste\.?\b/i, 'Suite ' )
|
753
|
+
_address.gsub!( /\bst\.?\b/i, 'Street ' )
|
754
|
+
_address.gsub!( /\btrpk\.?\b/i, 'Turnpike ' )
|
755
|
+
_address.gsub!( /\btr\.?\b/i, 'Trail ' )
|
756
|
+
|
757
|
+
# directions
|
758
|
+
_address.gsub!( /\bN\.?e\.?\b/i, 'Northeast ' )
|
759
|
+
_address.gsub!( /\bS\.?e\.?\b/i, 'Southeast ' )
|
760
|
+
_address.gsub!( /\bS\.?w\.?\b/i, 'Southwest ' )
|
761
|
+
_address.gsub!( /\bN\.?w\.?\b/i, 'Northwest ' )
|
762
|
+
_address.gsub!( /\bN\.?\b/, 'North ' )
|
763
|
+
_address.gsub!( /\bE\.?\b/, 'East ' )
|
764
|
+
_address.gsub!( /\bS\.?\b/, 'South ' )
|
765
|
+
_address.gsub!( /\bW\.?\b/, 'West ' )
|
766
|
+
_address.gsub!( '.', '' )
|
767
|
+
_address.gsub!( / +/, ' ' )
|
768
|
+
_address.strip
|
769
|
+
end
|
770
|
+
|
771
|
+
def formatted_phone
|
772
|
+
if self
|
773
|
+
# remove non-digit characters
|
774
|
+
_self = self.gsub(/[\(\) -]+/, '')
|
775
|
+
# format as phone if 10 digits are left
|
776
|
+
return number_to_phone(_self, :area_code => true ) if !! (_self =~ /[0-9]{10}/)
|
777
|
+
end
|
778
|
+
|
779
|
+
self
|
780
|
+
end
|
781
|
+
|
782
|
+
def formatted_zip
|
783
|
+
return if self.blank?
|
784
|
+
self.gsub!( /[\(\) -]+/, '' )
|
785
|
+
self.size == 9 ? "#{self[0 .. 4]}-#{self[5 .. -1]}" : self
|
786
|
+
end
|
787
|
+
|
788
|
+
# Time methods
|
789
|
+
|
790
|
+
def to_12_hour_time
|
791
|
+
(self == '0' || self.blank?) ? nil : Time.parse( "#{self[0..-3]}:#{self[-2..-1]}" ).to_s( :time ).gsub(/^0/, '')
|
792
|
+
end
|
793
|
+
|
794
|
+
# URL methods
|
795
|
+
|
796
|
+
# Prefixes the given url with 'http://'.
|
797
|
+
def add_http_prefix
|
798
|
+
return if self.blank?
|
799
|
+
_uri = self.to_uri
|
800
|
+
return self if _uri.nil? || _uri.is_a?(URI::FTP) || _uri.is_a?(URI::HTTP) || _uri.is_a?(URI::HTTPS) || _uri.is_a?(URI::LDAP) || _uri.is_a?(URI::MailTo)
|
801
|
+
"http://#{self}"
|
802
|
+
end
|
803
|
+
|
804
|
+
# Returns true if a given string begins with http:// or https://.
|
805
|
+
def has_http?
|
806
|
+
!! (self =~ /^http[s]?:\/\/.+/)
|
807
|
+
end
|
808
|
+
|
809
|
+
# Returns true if a given string has a trailing slash.
|
810
|
+
def has_trailing_slash?
|
811
|
+
!! (self =~ /\/$/)
|
812
|
+
end
|
813
|
+
|
814
|
+
# Returns true if a given string refers to an HTML page.
|
815
|
+
def is_page?
|
816
|
+
!! (self =~ /\.htm[l]?$/)
|
817
|
+
end
|
818
|
+
|
819
|
+
# Returns the host from a given URL string; returns nil if the string is not a valid URL.
|
820
|
+
def to_host
|
821
|
+
_uri = self.to_uri
|
822
|
+
_uri ? _uri.host : nil
|
823
|
+
end
|
824
|
+
|
825
|
+
# Returns a URI for the given string; nil if the string is invalid.
|
826
|
+
def to_uri
|
827
|
+
begin
|
828
|
+
_uri = URI.parse self
|
829
|
+
rescue URI::InvalidURIError
|
830
|
+
RAILS_DEFAULT_LOGGER.warn "#{self} is an invalid URI!"
|
831
|
+
end
|
832
|
+
|
833
|
+
_uri
|
834
|
+
end
|
835
|
+
|
836
|
+
# Returns true if the given string is a valid URL.
|
837
|
+
def valid_http_url?
|
838
|
+
self.scan(/:\/\//).size == 1 && self.to_uri.is_a?(URI::HTTP)
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
842
|
+
class StringHelperSingleton
|
843
|
+
include Singleton
|
844
|
+
include ActionView::Helpers::NumberHelper
|
845
|
+
end
|
846
|
+
|
847
|
+
class TrueClass
|
848
|
+
def to_i; 1; end
|
849
|
+
end
|
850
|
+
|
851
|
+
class FalseClass
|
852
|
+
def to_i; 0; end
|
853
|
+
end
|
data/mir_utility.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{mir_utility}
|
5
|
+
s.version = "0.3.29"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Corey Ehmke and Rod Monje"]
|
9
|
+
s.date = %q{2010-10-21}
|
10
|
+
s.description = %q{Standard extensions for Mir Rails apps.}
|
11
|
+
s.email = %q{corey@seologic.com}
|
12
|
+
s.extra_rdoc_files = ["README.rdoc", "lib/mir_form_builder.rb", "lib/mir_utility.rb"]
|
13
|
+
s.files = ["Manifest", "README.rdoc", "Rakefile", "init.rb", "lib/mir_form_builder.rb", "lib/mir_utility.rb", "mir_utility.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/bantik/mir_utility}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Mir_utility", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{mir_utility}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{Standard extensions for Mir Rails apps.}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mir_utility
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 41
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 29
|
10
|
+
version: 0.3.29
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Corey Ehmke and Rod Monje
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-10-21 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Standard extensions for Mir Rails apps.
|
23
|
+
email: corey@seologic.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README.rdoc
|
30
|
+
- lib/mir_form_builder.rb
|
31
|
+
- lib/mir_utility.rb
|
32
|
+
files:
|
33
|
+
- Manifest
|
34
|
+
- README.rdoc
|
35
|
+
- Rakefile
|
36
|
+
- init.rb
|
37
|
+
- lib/mir_form_builder.rb
|
38
|
+
- lib/mir_utility.rb
|
39
|
+
- mir_utility.gemspec
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/bantik/mir_utility
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --line-numbers
|
47
|
+
- --inline-source
|
48
|
+
- --title
|
49
|
+
- Mir_utility
|
50
|
+
- --main
|
51
|
+
- README.rdoc
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
hash: 11
|
69
|
+
segments:
|
70
|
+
- 1
|
71
|
+
- 2
|
72
|
+
version: "1.2"
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project: mir_utility
|
76
|
+
rubygems_version: 1.3.7
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: Standard extensions for Mir Rails apps.
|
80
|
+
test_files: []
|
81
|
+
|