mir_extensions 0.2.0
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/.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
|