mir_extensions 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.bundle/config +2 -0
- data/.document +5 -0
- data/.gitignore +21 -0
- data/.rspec +1 -0
- data/Gemfile +41 -0
- data/Gemfile.lock +116 -0
- data/LICENSE +20 -0
- data/README +256 -0
- data/README.rdoc +17 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/app/controllers/application_controller.rb +9 -0
- data/app/helpers/application_helper.rb +2 -0
- data/app/models/primary.rb +11 -0
- data/app/models/secondary.rb +7 -0
- data/app/views/layouts/application.html.erb +14 -0
- data/autotest/discover.rb +2 -0
- data/config.ru +4 -0
- data/config/application.rb +47 -0
- data/config/boot.rb +13 -0
- data/config/database.yml +17 -0
- data/config/environment.rb +5 -0
- data/config/environments/development.rb +22 -0
- data/config/environments/production.rb +49 -0
- data/config/environments/test.rb +35 -0
- data/config/initializers/backtrace_silencers.rb +7 -0
- data/config/initializers/inflections.rb +10 -0
- data/config/initializers/mime_types.rb +5 -0
- data/config/initializers/secret_token.rb +7 -0
- data/config/initializers/session_store.rb +8 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +58 -0
- data/db/development.sqlite3 +1 -0
- data/db/mir_ext_development.sqlite3 +0 -0
- data/db/mir_ext_test.sqlite3 +0 -0
- data/db/schema.rb +26 -0
- data/db/seeds.rb +7 -0
- data/db/test.sqlite3 +0 -0
- data/doc/README_FOR_APP +2 -0
- data/lib/core_ext/controller_extensions.rb +37 -0
- data/lib/core_ext/core_ext.rb +344 -0
- data/lib/core_ext/helper_extensions.rb +383 -0
- data/lib/mir_extensions.rb +37 -0
- data/lib/tasks/.gitkeep +0 -0
- data/log/development.log +151 -0
- data/log/production.log +0 -0
- data/log/server.log +0 -0
- data/log/test.log +27 -0
- data/mir_extensions.gemspec +119 -0
- data/public/404.html +26 -0
- data/public/422.html +26 -0
- data/public/500.html +26 -0
- data/public/favicon.ico +0 -0
- data/public/images/rails.png +0 -0
- data/public/index.html +262 -0
- data/public/javascripts/application.js +2 -0
- data/public/javascripts/controls.js +965 -0
- data/public/javascripts/dragdrop.js +974 -0
- data/public/javascripts/effects.js +1123 -0
- data/public/javascripts/prototype.js +6001 -0
- data/public/javascripts/rails.js +175 -0
- data/public/robots.txt +5 -0
- data/public/stylesheets/.gitkeep +0 -0
- data/script/rails +6 -0
- data/spec/controllers/application_controller_spec.rb +41 -0
- data/spec/helpers/application_helper_spec.rb +40 -0
- data/spec/mir_extensions_spec.rb +269 -0
- data/spec/spec_helper.rb +27 -0
- data/vendor/plugins/.gitkeep +0 -0
- metadata +170 -0
@@ -0,0 +1 @@
|
|
1
|
+
S
|
Binary file
|
Binary file
|
data/db/schema.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
4
|
+
#
|
5
|
+
# Note that this schema.rb definition is the authoritative source for your
|
6
|
+
# database schema. If you need to create the application database on another
|
7
|
+
# system, you should be using db:schema:load, not running all the migrations
|
8
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
9
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
10
|
+
#
|
11
|
+
# It's strongly recommended to check this file into your version control system.
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define(:version => 0) do
|
14
|
+
|
15
|
+
create_table "primaries", :force => true do |t|
|
16
|
+
t.string "name"
|
17
|
+
end
|
18
|
+
|
19
|
+
create_table "secondaries", :force => true do |t|
|
20
|
+
t.string "name"
|
21
|
+
t.integer "primary_id"
|
22
|
+
end
|
23
|
+
|
24
|
+
add_index "secondaries", ["primary_id"], :name => "index_roles_on_name"
|
25
|
+
|
26
|
+
end
|
data/db/seeds.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# This file should contain all the record creation needed to seed the database with its default values.
|
2
|
+
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
|
3
|
+
#
|
4
|
+
# Examples:
|
5
|
+
#
|
6
|
+
# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
|
7
|
+
# Mayor.create(:name => 'Daley', :city => cities.first)
|
data/db/test.sqlite3
ADDED
Binary file
|
data/doc/README_FOR_APP
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
class ActionController::Base
|
2
|
+
require 'mir_extensions'
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
def self.local_ip
|
6
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
7
|
+
|
8
|
+
UDPSocket.open do |s|
|
9
|
+
s.connect '64.233.187.99', 1
|
10
|
+
s.addr.last
|
11
|
+
end
|
12
|
+
ensure
|
13
|
+
Socket.do_not_reverse_lookup = orig
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns a sanitized column parameter suitable for SQL order-by clauses.
|
17
|
+
def sanitize_by_param(allowed=[], default='id')
|
18
|
+
sanitize_params params && params[:by], allowed, default
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns a sanitized direction parameter suitable for SQL order-by clauses.
|
22
|
+
def sanitize_dir_param
|
23
|
+
sanitize_params params && params[:dir], ['ASC', 'DESC'], 'ASC'
|
24
|
+
end
|
25
|
+
|
26
|
+
# Use this method to prevent SQL injection vulnerabilities by verifying that a user-provided
|
27
|
+
# parameter is on a whitelist of allowed values.
|
28
|
+
#
|
29
|
+
# Accepts a value, a list of allowed values, and a default value.
|
30
|
+
# Returns the value if allowed, otherwise the default.
|
31
|
+
def sanitize_params(supplied='', allowed=[], default=nil)
|
32
|
+
raise ArgumentError, "A default value is required." unless default
|
33
|
+
return default if supplied.blank? || allowed.blank? || ! allowed.include?(supplied)
|
34
|
+
return supplied
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,344 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'singleton'
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/controller_extensions')
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/helper_extensions')
|
5
|
+
require 'soap/header/simplehandler'
|
6
|
+
|
7
|
+
module CoreExt
|
8
|
+
|
9
|
+
# String Extensions ==============================================================================
|
10
|
+
String.class_eval do
|
11
|
+
|
12
|
+
include ActionView::Helpers::NumberHelper
|
13
|
+
|
14
|
+
# Usage: "3125552312".to_phone
|
15
|
+
# Returns: 312-555-2313
|
16
|
+
#
|
17
|
+
# Usage: "3125552313".to_phone(:area_code => true)
|
18
|
+
# Returns : (312) 555-2313
|
19
|
+
#
|
20
|
+
def to_phone(options = {})
|
21
|
+
number_to_phone(self.to_i, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class String
|
28
|
+
|
29
|
+
# Bring in support for view helpers
|
30
|
+
# include MirExtensions::CoreExt::String::NumberHelper
|
31
|
+
|
32
|
+
# General methods
|
33
|
+
|
34
|
+
def capitalize_words
|
35
|
+
self.downcase.gsub(/\b([a-z])/) { $1.capitalize }.gsub( "'S", "'s" )
|
36
|
+
end
|
37
|
+
|
38
|
+
# Address methods
|
39
|
+
|
40
|
+
def expand_address_abbreviations
|
41
|
+
_address = self.strip.capitalize_words
|
42
|
+
|
43
|
+
# NOTE: DO NOT rearrange the replace sequences; order matters!
|
44
|
+
|
45
|
+
# streets
|
46
|
+
_address.gsub!( /\b(ave|av)\.?\b/i, 'Avenue ' )
|
47
|
+
_address.gsub!( /\b(blvd|blv|bld|bl)\.?\b/i, 'Boulevard ' )
|
48
|
+
_address.gsub!( /\bcr\.?\b/i, 'Circle ' )
|
49
|
+
_address.gsub!( /\bctr\.?\b/i, 'Center ' )
|
50
|
+
_address.gsub!( /\b(crt|ct)\.?\b/i, 'Court ' )
|
51
|
+
_address.gsub!( /\bdr\.?\b/i, 'Drive ' )
|
52
|
+
_address.gsub!( /\b(expressw|expw|expy)\.?\b/i, 'Expressway ' )
|
53
|
+
_address.gsub!( /\bfrwy\.?\b/i, 'Freeway ' )
|
54
|
+
_address.gsub!( /\bhwy\.?\b/i, 'Highway ' )
|
55
|
+
_address.gsub!( /\bln\.?\b/i, 'Lane ' )
|
56
|
+
_address.gsub!( /\b(prkwy|pkwy|pkw|pky)\.?\b/i, 'Parkway ' )
|
57
|
+
_address.gsub!( /\bpk\.?\b/i, 'Pike ' )
|
58
|
+
_address.gsub!( /\bplz\.?\b/i, 'Plaza ' )
|
59
|
+
_address.gsub!( /\bpl\.?\b/i, 'Place ' )
|
60
|
+
_address.gsub!( /\brd\.?\b/i, 'Road ' )
|
61
|
+
_address.gsub!( /\b(rte|rt)\.?\b/i, 'Route ' )
|
62
|
+
_address.gsub!( /\bste\.?\b/i, 'Suite ' )
|
63
|
+
_address.gsub!( /\bst\.?\b/i, 'Street ' )
|
64
|
+
_address.gsub!( /\btrpk\.?\b/i, 'Turnpike ' )
|
65
|
+
_address.gsub!( /\btr\.?\b/i, 'Trail ' )
|
66
|
+
|
67
|
+
# directions
|
68
|
+
_address.gsub!( /\bN\.?e\.?\b/i, 'Northeast ' )
|
69
|
+
_address.gsub!( /\bS\.?e\.?\b/i, 'Southeast ' )
|
70
|
+
_address.gsub!( /\bS\.?w\.?\b/i, 'Southwest ' )
|
71
|
+
_address.gsub!( /\bN\.?w\.?\b/i, 'Northwest ' )
|
72
|
+
_address.gsub!( /\bN\.?\b/, 'North ' )
|
73
|
+
_address.gsub!( /\bE\.?\b/, 'East ' )
|
74
|
+
_address.gsub!( /\bS\.?\b/, 'South ' )
|
75
|
+
_address.gsub!( /\bW\.?\b/, 'West ' )
|
76
|
+
_address.gsub!( '.', '' )
|
77
|
+
_address.gsub!( / +/, ' ' )
|
78
|
+
_address.strip
|
79
|
+
end
|
80
|
+
|
81
|
+
def formatted_phone
|
82
|
+
if self
|
83
|
+
# remove non-digit characters
|
84
|
+
_self = self.gsub(/[\(\) -]+/, '')
|
85
|
+
# format as phone if 10 digits are left
|
86
|
+
return number_to_phone(_self, :area_code => true ) if !! (_self =~ /[0-9]{10}/)
|
87
|
+
end
|
88
|
+
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
def formatted_zip
|
93
|
+
return if self.blank?
|
94
|
+
self.gsub!( /[\(\) -]+/, '' )
|
95
|
+
self.size == 9 ? "#{self[0 .. 4]}-#{self[5 .. -1]}" : self
|
96
|
+
end
|
97
|
+
|
98
|
+
# Time methods
|
99
|
+
|
100
|
+
def to_12_hour_time
|
101
|
+
(self == '0' || self.blank?) ? nil : Time.parse( "#{self[0..-3]}:#{self[-2..-1]}" ).to_s( :time ).gsub(/^0/, '')
|
102
|
+
end
|
103
|
+
|
104
|
+
# URL methods
|
105
|
+
|
106
|
+
# Prefixes the given url with 'http://'.
|
107
|
+
def add_http_prefix
|
108
|
+
return if self.blank?
|
109
|
+
_uri = self.to_uri
|
110
|
+
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)
|
111
|
+
"http://#{self}"
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns true if a given string begins with http:// or https://.
|
115
|
+
def has_http?
|
116
|
+
!! (self =~ /^http[s]?:\/\/.+/)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns true if a given string has a trailing slash.
|
120
|
+
def has_trailing_slash?
|
121
|
+
!! (self =~ /\/$/)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns true if a given string refers to an HTML page.
|
125
|
+
def is_page?
|
126
|
+
!! (self =~ /\.htm[l]?$/)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns the host from a given URL string; returns nil if the string is not a valid URL.
|
130
|
+
def to_host
|
131
|
+
_uri = self.to_uri
|
132
|
+
_uri ? _uri.host : nil
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns a URI for the given string; nil if the string is invalid.
|
136
|
+
def to_uri
|
137
|
+
begin
|
138
|
+
_uri = URI.parse self
|
139
|
+
rescue URI::InvalidURIError
|
140
|
+
_uri = nil
|
141
|
+
end
|
142
|
+
|
143
|
+
_uri
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns true if the given string is a valid URL.
|
147
|
+
def valid_http_url?
|
148
|
+
self.scan(/:\/\//).size == 1 && self.to_uri.is_a?(URI::HTTP)
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
# Array Extensions ===============================================================================
|
154
|
+
class Array
|
155
|
+
def mean
|
156
|
+
self.inject(0){ |sum, x| sum += x } / self.size.to_f
|
157
|
+
end
|
158
|
+
|
159
|
+
def count
|
160
|
+
self.size
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
# Enumerable Extensions ==========================================================================
|
166
|
+
module Enumerable
|
167
|
+
def to_histogram
|
168
|
+
inject(Hash.new(0)) { |h,x| h[x] += 1; h }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Fixnum Extensions ==============================================================================
|
173
|
+
class Fixnum
|
174
|
+
|
175
|
+
# Given a number of seconds, convert into a string like HH:MM:SS
|
176
|
+
def to_hrs_mins_secs
|
177
|
+
_now = DateTime.now
|
178
|
+
_d = Date::day_fraction_to_time((_now + self.seconds) - _now)
|
179
|
+
"#{sprintf('%02d',_d[0])}:#{sprintf('%02d',_d[1])}:#{sprintf('%02d',_d[2])}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Float Extensions ===============================================================================
|
184
|
+
class Float
|
185
|
+
def to_nearest_tenth
|
186
|
+
sprintf("%.1f", self).to_f
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Hash Extensions ================================================================================
|
191
|
+
class Hash
|
192
|
+
def to_params
|
193
|
+
params = ''
|
194
|
+
stack = []
|
195
|
+
|
196
|
+
each do |k, v|
|
197
|
+
if v.is_a?(Hash)
|
198
|
+
stack << [k,v]
|
199
|
+
elsif v.is_a?(Array)
|
200
|
+
stack << [k,Hash.from_array(v)]
|
201
|
+
else
|
202
|
+
params << "#{k}=#{v}&"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
stack.each do |parent, hash|
|
207
|
+
hash.each do |k, v|
|
208
|
+
if v.is_a?(Hash)
|
209
|
+
stack << ["#{parent}[#{k}]", v]
|
210
|
+
else
|
211
|
+
params << "#{parent}[#{k}]=#{v}&"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
params.chop!
|
217
|
+
params
|
218
|
+
end
|
219
|
+
|
220
|
+
def to_sql( operator = 'AND' )
|
221
|
+
_sql = self.keys.map do |_key|
|
222
|
+
_value = self[_key].is_a?(Fixnum) ? self[_key] : "'#{self[_key]}'"
|
223
|
+
self[_key].nil? ? '1 = 1' : "#{_key} = #{_value}"
|
224
|
+
end
|
225
|
+
_sql * " #{operator} "
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.from_array(array = [])
|
229
|
+
h = Hash.new
|
230
|
+
array.size.times{ |t| h[t] = array[t] }
|
231
|
+
h
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
# SOAP Extensions ================================================================================
|
237
|
+
class Header < SOAP::Header::SimpleHandler
|
238
|
+
def initialize(tag, value)
|
239
|
+
super(XSD::QName.new(nil, tag))
|
240
|
+
@tag = tag
|
241
|
+
@value = value
|
242
|
+
end
|
243
|
+
|
244
|
+
def on_simple_outbound
|
245
|
+
@value
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class ActiveRecord::Base
|
250
|
+
|
251
|
+
#FIXME Extending AR in this way will stop working under Rails 2.3.2 for some reason.
|
252
|
+
|
253
|
+
# scope :order_by, lambda{ |col, dir| {:order => (col.blank?) ? ( (dir.blank?) ? 'id' : dir ) : "#{col} #{dir}"} }
|
254
|
+
# scope :limit, lambda { |num| { :limit => num } }
|
255
|
+
|
256
|
+
# TODO: call the column_names class method on the subclass
|
257
|
+
# 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)}"} }
|
258
|
+
|
259
|
+
# Returns an array of SQL conditions suitable for use with ActiveRecord's finder.
|
260
|
+
# valid_criteria is an array of valid search fields.
|
261
|
+
# pairs is a hash of field names and values.
|
262
|
+
def self.search_conditions( valid_search_criteria, pairs, operator = 'OR' )
|
263
|
+
if valid_search_criteria.detect{ |_c| ! pairs[_c].blank? } || ! pairs[:query].blank?
|
264
|
+
_conditions = []
|
265
|
+
_or_clause = ''
|
266
|
+
_or_clause_values = []
|
267
|
+
_int_terms = {}
|
268
|
+
_text_terms = {}
|
269
|
+
|
270
|
+
# build or clause for keyword search
|
271
|
+
unless pairs[:query].blank? || ! self.respond_to?(:flattened_content)
|
272
|
+
pairs[:query].split(' ').each do |keyword|
|
273
|
+
_or_clause += 'flattened_content LIKE ? OR '
|
274
|
+
_or_clause_values << "%#{keyword}%"
|
275
|
+
end
|
276
|
+
|
277
|
+
_or_clause.gsub!( / OR $/, '')
|
278
|
+
end
|
279
|
+
|
280
|
+
# iterate across each valid search field
|
281
|
+
valid_search_criteria.each do |_field|
|
282
|
+
# build or clause for keyword search
|
283
|
+
unless pairs[:query].blank? || self.respond_to?(:flattened_content)
|
284
|
+
pairs[:query].split(' ').each do |keyword|
|
285
|
+
_or_clause += "#{_field} LIKE ? OR "
|
286
|
+
_or_clause_values << "%#{keyword}%"
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# build hashes of integer and/or text search fields and values for each non-blank param
|
291
|
+
if ! pairs[_field].blank?
|
292
|
+
_field.to_s =~ /^id$|_id$|\?$/ ? _int_terms[_field.to_s.gsub('?', '')] = pairs[_field] : _text_terms[_field] = pairs[_field]
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
_or_clause.gsub!( / OR $/, '')
|
297
|
+
|
298
|
+
# convert the hash to parametric SQL
|
299
|
+
if _or_clause.blank?
|
300
|
+
_conditions = sql_conditions_for( _int_terms, _text_terms, nil, operator )
|
301
|
+
elsif _int_terms.keys.empty? && _text_terms.keys.empty?
|
302
|
+
_conditions = [ _or_clause ]
|
303
|
+
else
|
304
|
+
_conditions = sql_conditions_for( _int_terms, _text_terms, _or_clause, operator )
|
305
|
+
end
|
306
|
+
|
307
|
+
# add integer values
|
308
|
+
_int_terms.keys.each{ |key| _conditions << _int_terms[key] }
|
309
|
+
# add wildcard-padded values
|
310
|
+
_text_terms.keys.each{ |key| _conditions << "%#{_text_terms[key]}%" }
|
311
|
+
|
312
|
+
unless _or_clause_values.empty?
|
313
|
+
# add keywords
|
314
|
+
_conditions += _or_clause_values
|
315
|
+
end
|
316
|
+
|
317
|
+
return _conditions
|
318
|
+
else
|
319
|
+
return nil
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
def self.to_option_values
|
324
|
+
self.all.map{ |_x| [_x.name, _x.id] }
|
325
|
+
end
|
326
|
+
|
327
|
+
# Strips the specified attribute's value.
|
328
|
+
def strip(attribute)
|
329
|
+
value = self[attribute]
|
330
|
+
self.send("#{attribute}=", value && value.strip)
|
331
|
+
end
|
332
|
+
|
333
|
+
private
|
334
|
+
|
335
|
+
def self.sql_conditions_for( integer_fields, text_fields, or_clause = nil, operator = 'OR' )
|
336
|
+
if integer_fields.empty? && ! text_fields.empty?
|
337
|
+
[ text_fields.keys.map{ |k| k } * " LIKE ? #{operator} " + ' LIKE ?' + (or_clause ? " #{operator} #{or_clause}" : '') ]
|
338
|
+
elsif ! integer_fields.empty? && text_fields.empty?
|
339
|
+
[ integer_fields.keys.map{ |k| k } * " = ? #{operator} " + ' = ?' + (or_clause ? " #{operator} #{or_clause}" : '') ]
|
340
|
+
else
|
341
|
+
[ integer_fields.keys.map{ |k| k } * " = ? #{operator} " + " = ? #{operator} " + text_fields.keys.map{ |k| k } * " LIKE ? #{operator} " + ' LIKE ?' + (or_clause ? " #{operator} #{or_clause}" : '') ]
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
@@ -0,0 +1,383 @@
|
|
1
|
+
module ActionController
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
def action?( expression )
|
5
|
+
!! ( expression.class == Regexp ? controller.action_name =~ expression : controller.action_name == expression )
|
6
|
+
end
|
7
|
+
|
8
|
+
# Formats an array with HTML line breaks, or the specified delimiter.
|
9
|
+
def array_to_lines(array, delimiter = '<br />')
|
10
|
+
array.blank? ? nil : array * delimiter
|
11
|
+
end
|
12
|
+
|
13
|
+
def checkmark
|
14
|
+
%{<div class="checkmark"></div>}
|
15
|
+
end
|
16
|
+
|
17
|
+
def controller?( expression )
|
18
|
+
!! ( expression.class == Regexp ? controller.controller_name =~ expression : controller.controller_name == expression )
|
19
|
+
end
|
20
|
+
|
21
|
+
# Display CRUD icons or links, according to setting in use_crud_icons method.
|
22
|
+
#
|
23
|
+
# In application_helper.rb:
|
24
|
+
#
|
25
|
+
# def use_crud_icons
|
26
|
+
# true
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# Then use in index views like this:
|
30
|
+
#
|
31
|
+
# <td class="crud_links"><%= crud_links(my_model, 'my_model', [:show, :edit, :delete]) -%></td>
|
32
|
+
#
|
33
|
+
def crud_links(model, instance_name, actions, args={})
|
34
|
+
_html = ""
|
35
|
+
_options = args.keys.empty? ? '' : ", #{args.map{|k,v| ":#{k} => #{v}"}}"
|
36
|
+
|
37
|
+
if use_crud_icons
|
38
|
+
if actions.include?(:show)
|
39
|
+
_html << eval("link_to image_tag('/images/icons/view.png', :class => 'crud_icon'), model, :title => 'View'#{_options}")
|
40
|
+
end
|
41
|
+
if actions.include?(:edit)
|
42
|
+
_html << eval("link_to image_tag('/images/icons/edit.png', :class => 'crud_icon'), edit_#{instance_name}_path(model), :title => 'Edit'#{_options}")
|
43
|
+
end
|
44
|
+
if actions.include?(:delete)
|
45
|
+
_html << eval("link_to image_tag('/images/icons/delete.png', :class => 'crud_icon'), model, :confirm => 'Are you sure? This action cannot be undone.', :method => :delete, :title => 'Delete'#{_options}")
|
46
|
+
end
|
47
|
+
else
|
48
|
+
if actions.include?(:show)
|
49
|
+
_html << eval("link_to 'View', model, :title => 'View', :class => 'crud_link'#{_options}")
|
50
|
+
end
|
51
|
+
if actions.include?(:edit)
|
52
|
+
_html << eval("link_to 'Edit', edit_#{instance_name}_path(model), :title => 'Edit', :class => 'crud_link'#{_options}")
|
53
|
+
end
|
54
|
+
if actions.include?(:delete)
|
55
|
+
_html << eval("link_to 'Delete', model, :confirm => 'Are you sure? This action cannot be undone.', :method => :delete, :title => 'Delete', :class => 'crud_link'#{_options}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
_html
|
59
|
+
end
|
60
|
+
|
61
|
+
# Display CRUD icons or links, according to setting in use_crud_icons method.
|
62
|
+
# This method works with nested resources.
|
63
|
+
# Use in index views like this:
|
64
|
+
#
|
65
|
+
# <td class="crud_links"><%= crud_links_for_nested_resource(@my_model, my_nested_model, 'my_model', 'my_nested_model', [:show, :edit, :delete]) -%></td>
|
66
|
+
#
|
67
|
+
def crud_links_for_nested_resource(model, nested_model, model_instance_name, nested_model_instance_name, actions, args={})
|
68
|
+
_html = ""
|
69
|
+
if use_crud_icons
|
70
|
+
if actions.include?(:show)
|
71
|
+
_html << eval("link_to image_tag('/images/icons/view.png', :class => 'crud_icon'), #{model_instance_name}_#{nested_model_instance_name}_path(model, nested_model), :title => 'View'")
|
72
|
+
end
|
73
|
+
|
74
|
+
if actions.include?(:edit)
|
75
|
+
_html << eval("link_to image_tag('/images/icons/edit.png', :class => 'crud_icon'), edit_#{model_instance_name}_#{nested_model_instance_name}_path(model, nested_model), :title => 'Edit'")
|
76
|
+
end
|
77
|
+
|
78
|
+
if actions.include?(:delete)
|
79
|
+
_html << eval("link_to image_tag('/images/icons/delete.png', :class => 'crud_icon'), #{model_instance_name}_#{nested_model_instance_name}_path(model, nested_model), :method => :delete, :confirm => 'Are you sure? This action cannot be undone.', :title => 'Delete'")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
_html
|
83
|
+
end
|
84
|
+
|
85
|
+
# DRY way to return a legend tag that renders correctly in all browsers. This variation allows
|
86
|
+
# for more "stuff" inside the legend tag, e.g. expand/collapse controls, without having to worry
|
87
|
+
# about escape sequences.
|
88
|
+
#
|
89
|
+
# Sample usage:
|
90
|
+
#
|
91
|
+
# <%- legend_block do -%>
|
92
|
+
# <span id="hide_or_show_backlinks" class="show_link" style="background-color: #999999;
|
93
|
+
# border: 1px solid #999999;" onclick="javascript:hide_or_show('backlinks');"></span>Backlinks (<%=
|
94
|
+
# @google_results.size -%>)
|
95
|
+
# <%- end -%>
|
96
|
+
#
|
97
|
+
def legend_block(&block)
|
98
|
+
concat content_tag(:div, capture(&block), :class => "faux_legend")
|
99
|
+
end
|
100
|
+
|
101
|
+
# DRY way to return a legend tag that renders correctly in all browsers
|
102
|
+
def legend_tag(text, args={})
|
103
|
+
_html = %{<div id="#{args[:id]}" class="faux_legend">#{text}</div>\r}
|
104
|
+
_html.gsub!(/ id=""/,'')
|
105
|
+
_html.gsub!(/ class=""/,'')
|
106
|
+
_html
|
107
|
+
end
|
108
|
+
|
109
|
+
def meta_description(content=nil)
|
110
|
+
content_for(:meta_description) { content } unless content.blank?
|
111
|
+
end
|
112
|
+
|
113
|
+
def meta_keywords(content=nil)
|
114
|
+
content_for(:meta_keywords) { content } unless content.blank?
|
115
|
+
end
|
116
|
+
|
117
|
+
def models_for_select( models, label = 'name' )
|
118
|
+
models.map{ |m| [m[label], m.id] }.sort_by{ |e| e[0] }
|
119
|
+
end
|
120
|
+
|
121
|
+
def options_for_array( a, selected = nil, prompt = select_prompt )
|
122
|
+
"<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
|
123
|
+
end
|
124
|
+
|
125
|
+
# Create a link that is opaque to search engine spiders.
|
126
|
+
def obfuscated_link_to(path, image, label, args={})
|
127
|
+
_html = %{<form action="#{path}" method="get" class="obfuscated_link">}
|
128
|
+
_html << %{ <fieldset><input alt="#{label}" src="#{image}" type="image" /></fieldset>}
|
129
|
+
args.each{ |k,v| _html << %{ <div><input id="#{k.to_s}" name="#{k}" type="hidden" value="#{v}" /></div>} }
|
130
|
+
_html << %{</form>}
|
131
|
+
_html
|
132
|
+
end
|
133
|
+
|
134
|
+
# Wraps the given HTML in Rails' default style to highlight validation errors, if any.
|
135
|
+
def required_field_helper( model, element, html )
|
136
|
+
if model && ! model.errors.empty? && element.is_required
|
137
|
+
return content_tag( :div, html, :class => 'fieldWithErrors' )
|
138
|
+
else
|
139
|
+
return html
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def select_prompt
|
144
|
+
"Select..."
|
145
|
+
end
|
146
|
+
|
147
|
+
def select_prompt_option
|
148
|
+
"<option value=''>#{select_prompt}</option>"
|
149
|
+
end
|
150
|
+
|
151
|
+
# Use on index pages to create dropdown list of filtering criteria.
|
152
|
+
# Populate the filter list using a constant in the model corresponding to named scopes.
|
153
|
+
#
|
154
|
+
# Usage:
|
155
|
+
#
|
156
|
+
# - item.rb:
|
157
|
+
#
|
158
|
+
# named_scope :active, :conditions => { :is_active => true }
|
159
|
+
# named_scope :inactive, :conditions => { :is_active => false }
|
160
|
+
#
|
161
|
+
# FILTERS = [
|
162
|
+
# {:scope => "all", :label => "All"},
|
163
|
+
# {:scope => "active", :label => "Active Only"},
|
164
|
+
# {:scope => "inactive", :label => "Inactive Only"}
|
165
|
+
# ]
|
166
|
+
#
|
167
|
+
# - items/index.html.erb:
|
168
|
+
#
|
169
|
+
# <%= select_tag_for_filter("items", @filters, params) -%>
|
170
|
+
#
|
171
|
+
# - items_controller.rb:
|
172
|
+
#
|
173
|
+
# def index
|
174
|
+
# @filters = Item::FILTERS
|
175
|
+
# if params[:show] && params[:show] != "all" && @filters.collect{|f| f[:scope]}.include?(params[:show])
|
176
|
+
# @items = eval("@items.#{params[:show]}.order_by(params[:by], params[:dir])")
|
177
|
+
# else
|
178
|
+
# @items = @items.order_by(params[:by], params[:dir])
|
179
|
+
# end
|
180
|
+
# ...
|
181
|
+
# end
|
182
|
+
#
|
183
|
+
def select_tag_for_filter(model, nvpairs, params)
|
184
|
+
return unless model && nvpairs && ! nvpairs.empty?
|
185
|
+
options = { :query => params[:query] }
|
186
|
+
_url = url_for(eval("#{model}_url(options)"))
|
187
|
+
_html = %{<label for="show">Show:</label><br />}
|
188
|
+
_html << %{<select name="show" id="show" onchange="window.location='#{_url}' + '?show=' + this.value">}
|
189
|
+
nvpairs.each do |pair|
|
190
|
+
_html << %{<option value="#{pair[:scope]}"}
|
191
|
+
if params[:show] == pair[:scope] || ((params[:show].nil? || params[:show].empty?) && pair[:scope] == "all")
|
192
|
+
_html << %{ selected="selected"}
|
193
|
+
end
|
194
|
+
_html << %{>#{pair[:label]}}
|
195
|
+
_html << %{</option>}
|
196
|
+
end
|
197
|
+
_html << %{</select>}
|
198
|
+
end
|
199
|
+
|
200
|
+
# Returns a link_to tag with sorting parameters that can be used with ActiveRecord.order_by.
|
201
|
+
#
|
202
|
+
# To use standard resources, specify the resources as a plural symbol:
|
203
|
+
# sort_link(:users, 'email', params)
|
204
|
+
#
|
205
|
+
# To use resources aliased with :as (in routes.rb), specify the aliased route as a string.
|
206
|
+
# sort_link('users_admin', 'email', params)
|
207
|
+
#
|
208
|
+
# You can override the link's label by adding a labels hash to your params in the controller:
|
209
|
+
# params[:labels] = {'user_id' => 'User'}
|
210
|
+
def sort_link(model, field, params, html_options={})
|
211
|
+
if (field.to_sym == params[:by] || field == params[:by]) && params[:dir] == "ASC"
|
212
|
+
classname = "arrow-asc"
|
213
|
+
dir = "DESC"
|
214
|
+
elsif (field.to_sym == params[:by] || field == params[:by])
|
215
|
+
classname = "arrow-desc"
|
216
|
+
dir = "ASC"
|
217
|
+
else
|
218
|
+
dir = "ASC"
|
219
|
+
end
|
220
|
+
|
221
|
+
options = {
|
222
|
+
:anchor => html_options[:anchor] || nil,
|
223
|
+
:by => field,
|
224
|
+
:dir => dir,
|
225
|
+
:query => params[:query],
|
226
|
+
:show => params[:show]
|
227
|
+
}
|
228
|
+
|
229
|
+
options[:show] = params[:show] unless params[:show].blank? || params[:show] == 'all'
|
230
|
+
|
231
|
+
html_options = {
|
232
|
+
:class => "#{classname} #{html_options[:class]}",
|
233
|
+
:style => "color: white; font-weight: #{params[:by] == field ? "bold" : "normal"}; #{html_options[:style]}",
|
234
|
+
:title => "Sort by this field"
|
235
|
+
}
|
236
|
+
|
237
|
+
field_name = params[:labels] && params[:labels][field] ? params[:labels][field] : field.titleize
|
238
|
+
|
239
|
+
_link = model.is_a?(Symbol) ? eval("#{model}_url(options)") : "/#{model}?#{options.to_params}"
|
240
|
+
link_to(field_name, _link, html_options)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Tabbed interface helpers =======================================================================
|
244
|
+
|
245
|
+
# Returns formatted tabs with appropriate JS for activation. Use in conjunction with tab_body.
|
246
|
+
#
|
247
|
+
# Usage:
|
248
|
+
#
|
249
|
+
# <%- tabset do -%>
|
250
|
+
# <%= tab_tag :id => 'ppc_ads', :label => 'PPC Ads', :state => 'active' %>
|
251
|
+
# <%= tab_tag :id => 'budget' %>
|
252
|
+
# <%= tab_tag :id => 'geotargeting' %>
|
253
|
+
# <%- end -%>
|
254
|
+
#
|
255
|
+
def tabset(&proc)
|
256
|
+
concat %{
|
257
|
+
<div class="jump_links">
|
258
|
+
<ul>
|
259
|
+
}
|
260
|
+
yield
|
261
|
+
concat %{
|
262
|
+
</ul>
|
263
|
+
</div>
|
264
|
+
<br style="clear: both;" /><br />
|
265
|
+
<input type="hidden" id="show_tab" />
|
266
|
+
<script type="text/javascript">
|
267
|
+
function hide_all_tabs() { $$('.tab_block').invoke('hide'); }
|
268
|
+
function activate_tab(tab) {
|
269
|
+
$$('.tab_control').each(function(elem){ elem.className = 'tab_control'});
|
270
|
+
$('show_' + tab).className = 'tab_control active';
|
271
|
+
hide_all_tabs();
|
272
|
+
$(tab).toggle();
|
273
|
+
$('show_tab').value = tab
|
274
|
+
}
|
275
|
+
function sticky_tab() { if (location.hash) { activate_tab(location.hash.gsub('#','')); } }
|
276
|
+
Event.observe(window, 'load', function() { sticky_tab(); });
|
277
|
+
</script>
|
278
|
+
}
|
279
|
+
end
|
280
|
+
|
281
|
+
# Returns a tab body corresponding to tabs in a tabset. Make sure that the id of the tab_body
|
282
|
+
# matches the id provided to the tab_tag in the tabset block.
|
283
|
+
#
|
284
|
+
# Usage:
|
285
|
+
#
|
286
|
+
# <%- tab_body :id => 'ppc_ads', :label => 'PPC Ad Details' do -%>
|
287
|
+
# PPC ads form here.
|
288
|
+
# <%- end -%>
|
289
|
+
#
|
290
|
+
# <%- tab_body :id => 'budget' do -%>
|
291
|
+
# Budget form here.
|
292
|
+
# <%- end -%>
|
293
|
+
#
|
294
|
+
# <%- tab_body :id => 'geotargeting' do -%>
|
295
|
+
# Geotargeting form here.
|
296
|
+
# <%- end -%>
|
297
|
+
#
|
298
|
+
def tab_body(args, &proc)
|
299
|
+
concat %{<div id="#{args[:id]}" class="tab_block form_container" style="display: #{args[:display] || 'none'};">}
|
300
|
+
concat %{#{legend_tag args[:label] || args[:id].titleize }}
|
301
|
+
concat %{<a name="#{args[:id]}"></a><br />}
|
302
|
+
yield
|
303
|
+
concat %{</div>}
|
304
|
+
end
|
305
|
+
|
306
|
+
# Returns the necessary HTML for a particular tab. Use inside a tabset block.
|
307
|
+
# Override the default tab label by specifying a :label parameter.
|
308
|
+
# Indicate that the tab should be active by setting its :state to 'active'.
|
309
|
+
# (NOTE: You must define a corresponding CSS style for active tabs.)
|
310
|
+
#
|
311
|
+
# Usage:
|
312
|
+
#
|
313
|
+
# <%= tab_tag :id => 'ppc_ads', :label => 'PPC Ads', :state => 'active' %>
|
314
|
+
#
|
315
|
+
def tab_tag(args, *css_class)
|
316
|
+
%{<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>}
|
317
|
+
end
|
318
|
+
|
319
|
+
# ================================================================================================
|
320
|
+
|
321
|
+
def tag_for_collapsible_row(obj, params)
|
322
|
+
_html = ""
|
323
|
+
if obj && obj.respond_to?(:parent) && obj.parent
|
324
|
+
_html << %{<tr class="#{obj.class.name.downcase}_#{obj.parent.id} #{params[:class]}" style="display: none; #{params[:style]}">}
|
325
|
+
else
|
326
|
+
_html << %{<tr class="#{params[:class]}" style="#{params[:style]}">}
|
327
|
+
end
|
328
|
+
_html
|
329
|
+
end
|
330
|
+
|
331
|
+
def tag_for_collapsible_row_control(obj)
|
332
|
+
_base_id = "#{obj.class.name.downcase}_#{obj.id}"
|
333
|
+
_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>}
|
334
|
+
end
|
335
|
+
|
336
|
+
# Create a set of tags for displaying a field label with inline help.
|
337
|
+
# Field label text is appended with a ? icon, which responds to a click
|
338
|
+
# by showing or hiding the provided help text.
|
339
|
+
#
|
340
|
+
# Sample usage:
|
341
|
+
#
|
342
|
+
# <%= 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.' %>
|
343
|
+
#
|
344
|
+
# Yields:
|
345
|
+
#
|
346
|
+
# <label for="rel_frequency">Relative Frequency: <%= image_tag "/images/help_icon.png", :onclick => "$('rel_frequency_help').toggle();", :class => 'inline_icon' %></label><br />
|
347
|
+
# <div class="inline_help" id="rel_frequency_help" style="display: none;">
|
348
|
+
# <p>Relative frequency of search traffic for this keyword across multiple search engines, as measured by WordTracker.</p>
|
349
|
+
# </div>
|
350
|
+
def tag_for_label_with_inline_help( label_text, field_id, help_text )
|
351
|
+
_html = ""
|
352
|
+
_html << %{<label for="#{field_id}">#{label_text}}
|
353
|
+
_html << %{<img src="/images/icons/help_icon.png" onclick="$('#{field_id}_help').toggle();" class='inline_icon' />}
|
354
|
+
_html << %{</label><br />}
|
355
|
+
_html << %{<div class="inline_help" id="#{field_id}_help" style="display: none;">}
|
356
|
+
_html << %{<p>#{help_text}</p>}
|
357
|
+
_html << %{</div>}
|
358
|
+
_html
|
359
|
+
end
|
360
|
+
|
361
|
+
# Create a set of tags for displaying a field label followed by instructions.
|
362
|
+
# The instructions are displayed on a new line following the field label.
|
363
|
+
#
|
364
|
+
# Usage:
|
365
|
+
#
|
366
|
+
# <%= tag_for_label_with_instructions 'Status', 'is_active', 'Only active widgets will be visible to the public.' %>
|
367
|
+
#
|
368
|
+
# Yields:
|
369
|
+
#
|
370
|
+
# <label for="is_active">
|
371
|
+
# Status<br />
|
372
|
+
# <span class="instructions">Only active widgets will be visible to the public.</span>
|
373
|
+
# <label><br />
|
374
|
+
def tag_for_label_with_instructions( label_text, field_id, instructions )
|
375
|
+
_html = ""
|
376
|
+
_html << %{<label for="#{field_id}">#{label_text}}
|
377
|
+
_html << %{<span class="instructions">#{instructions}</span>}
|
378
|
+
_html << %{</label><br />}
|
379
|
+
_html
|
380
|
+
end
|
381
|
+
|
382
|
+
end
|
383
|
+
end
|