mir_utility 0.3.29
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|