kiss 1.1 → 1.7

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.
Files changed (45) hide show
  1. data/LICENSE +1 -1
  2. data/Rakefile +2 -1
  3. data/VERSION +1 -1
  4. data/bin/kiss +151 -34
  5. data/data/scaffold.tgz +0 -0
  6. data/lib/kiss.rb +389 -742
  7. data/lib/kiss/accessors/controller.rb +47 -0
  8. data/lib/kiss/accessors/request.rb +106 -0
  9. data/lib/kiss/accessors/template.rb +23 -0
  10. data/lib/kiss/action.rb +502 -132
  11. data/lib/kiss/bench.rb +14 -5
  12. data/lib/kiss/debug.rb +14 -6
  13. data/lib/kiss/exception_report.rb +22 -299
  14. data/lib/kiss/ext/core.rb +700 -0
  15. data/lib/kiss/ext/rack.rb +33 -0
  16. data/lib/kiss/ext/sequel_database.rb +47 -0
  17. data/lib/kiss/ext/sequel_mysql_dataset.rb +23 -0
  18. data/lib/kiss/form.rb +404 -179
  19. data/lib/kiss/form/field.rb +183 -307
  20. data/lib/kiss/form/field_types.rb +239 -0
  21. data/lib/kiss/format.rb +88 -70
  22. data/lib/kiss/html/exception_report.css +222 -0
  23. data/lib/kiss/html/exception_report.html +210 -0
  24. data/lib/kiss/iterator.rb +14 -12
  25. data/lib/kiss/login.rb +8 -8
  26. data/lib/kiss/mailer.rb +68 -66
  27. data/lib/kiss/model.rb +323 -36
  28. data/lib/kiss/rack/bench.rb +16 -8
  29. data/lib/kiss/rack/email_errors.rb +25 -15
  30. data/lib/kiss/rack/errors_ok.rb +2 -2
  31. data/lib/kiss/rack/facebook.rb +6 -6
  32. data/lib/kiss/rack/file_not_found.rb +10 -8
  33. data/lib/kiss/rack/log_exceptions.rb +3 -3
  34. data/lib/kiss/rack/recorder.rb +2 -2
  35. data/lib/kiss/rack/show_debug.rb +2 -2
  36. data/lib/kiss/rack/show_exceptions.rb +2 -2
  37. data/lib/kiss/request.rb +435 -0
  38. data/lib/kiss/sequel_session.rb +15 -14
  39. data/lib/kiss/static_file.rb +20 -13
  40. data/lib/kiss/template.rb +327 -0
  41. metadata +60 -25
  42. data/lib/kiss/controller_accessors.rb +0 -81
  43. data/lib/kiss/hacks.rb +0 -188
  44. data/lib/kiss/sequel_mysql.rb +0 -25
  45. data/lib/kiss/template_methods.rb +0 -167
@@ -1,25 +1,25 @@
1
1
  class Kiss
2
2
  class Login < Hash
3
3
  def initialize(session)
4
- @session = session
5
- @session['login'] ||= {}
4
+ @_session = session
5
+ @_session['login'] ||= {}
6
6
 
7
7
  # check if login expired
8
8
  if expired?
9
9
  # login expired
10
- @session['login'] = {}
10
+ @_session['login'] = {}
11
11
  end
12
12
 
13
- @persist_data = @session['login']
14
- self.merge!(@persist_data)
13
+ @_persist_data = @_session['login']
14
+ self.merge!(@_persist_data)
15
15
  end
16
16
 
17
17
  def expired?
18
- @session['login']['expires_at'] && session['login']['expires_at'] < Time.now
18
+ @_session['login']['expires_at'] && session['login']['expires_at'] < Time.now
19
19
  end
20
20
 
21
21
  def persist(data = {})
22
- @persist_data.merge!(data)
22
+ @_persist_data.merge!(data)
23
23
  self.merge!(data)
24
24
  end
25
25
 
@@ -38,7 +38,7 @@ class Kiss
38
38
  end
39
39
 
40
40
  def clear
41
- @session['login'] = {}
41
+ @_session['login'] = {}
42
42
  super()
43
43
  end
44
44
  end
@@ -1,7 +1,7 @@
1
1
  class Kiss
2
2
  # This class creates, renders, and sends email messages.
3
3
 
4
- # @options
4
+ # @_options
5
5
  # :server, :port, :domain, :account, :password, :auth - for SMTP login
6
6
  # :from/to - used by SMTP to send message (ignored by sendmail)
7
7
  # :message - message headers/body text
@@ -10,91 +10,93 @@ class Kiss
10
10
  # :sendmail_path - filesystem path to sendmail executable
11
11
 
12
12
  class Mailer
13
- include Kiss::TemplateMethods
13
+ @@sendmail_path = "/usr/sbin/sendmail -t"
14
+
15
+ class << self
16
+ def send(options)
17
+ if options[:sendmail] || (options[:engine] == :sendmail) || !options[:server]
18
+ send_via_sendmail(options)
19
+ else
20
+ send_via_smtp(options)
21
+ end
22
+ end
14
23
 
15
- attr_accessor :controller
24
+ # Attempts to send message using /usr/sbin/sendmail.
25
+ # NOTE: sendmail ignores :from and :to options, using
26
+ # From and To headers from the message
27
+ def send_via_sendmail(options)
28
+ puts options[:message]
29
+ IO.popen(options[:sendmail] || @@sendmail_path, "w") do |pipe|
30
+ pipe.puts(options[:message])
31
+ end
32
+ end
16
33
 
17
- def merge_options(options = {})
18
- @options.merge!(options)
19
- @controller = options[:controller] || @controller
20
- @data = options[:data] || @data
34
+ # Attempts to send message using Net::SMTP.
35
+ def send_via_smtp(options)
36
+ require 'net/smtp' unless defined?(Net::SMTP)
37
+
38
+ Net::SMTP.start(
39
+ options[:server] || 'localhost',
40
+ options[:port] || 25,
41
+ options[:domain] || nil,
42
+ options[:account] || options[:username] || nil,
43
+ options[:password] || nil,
44
+ options[:auth] || :plain
45
+ ) do |smtp|
46
+ smtp.sendmail(options[:message], options[:from], options[:to])
47
+ end
48
+ end
21
49
  end
22
50
 
51
+ include Kiss::ControllerAccessors
52
+ include Kiss::RequestAccessors
53
+ include Kiss::DatabaseAccessors
54
+ include Kiss::TemplateMethods
55
+
56
+ _attr_accessor :controller, :request, :options
57
+
23
58
  # Creates new email message object.
24
- def initialize(options = {})
25
- @options = {}
26
- @data = {}
59
+ def initialize(new_options = {})
60
+ @_options = {}
61
+ merge_options(new_options)
62
+ end
63
+
64
+ def merge_options(new_options = {})
65
+ if new_options[:controller]
66
+ @_controller = new_options[:controller]
67
+ @_options = @_controller.mailer_config.merge(@_options)
68
+ end
27
69
 
28
- merge_options(options) if options
70
+ @_options.merge!(new_options)
71
+ @_request = new_options[:request] || @_request
29
72
  end
30
73
 
31
74
  # Renders email template to string, unless message option is
32
75
  # already set to a string value.
33
- def prepare_email_message(options = nil)
34
- merge_options(options) if options
76
+ def prepare_email_message(new_options = {})
77
+ merge_options(new_options)
35
78
 
36
- unless @options[:message].is_a?(String)
37
- if template_name = @options[:template]
38
- @template_dir = controller.email_template_dir
39
- raise 'email_template_dir path not set' unless @template_dir
40
-
41
- data = vars = @options[:data] || @options[:vars]
42
-
43
- path = "#{@template_dir}/#{template_name}"
44
- @options[:message] = erubis(path,binding)
79
+ unless @_options[:message].is_a?(String)
80
+ if template = @_options[:template]
81
+ @@template_class ||= Kiss::Template.get_root_class(controller, controller.email_template_dir)
82
+ @data = @_options[:data] || @_options[:vars]
83
+ @_options[:message] = @@template_class.get_subclass_from_path('/'+template).new(request, self).call
45
84
  else
46
- raise 'email message not defined'
85
+ raise 'no email message or template found to prepare'
47
86
  end
48
87
  end
49
88
 
50
- return @options[:message]
89
+ return @_options[:message]
51
90
  end
52
91
 
53
92
  # Attempts to send message using SMTP, unless :engine option is set to
54
93
  # :sendmail.
55
- def send(options = nil)
56
- merge_options(options) if options
57
-
58
- if @options[:engine] == :sendmail
59
- return sendmail
60
- else
61
- return send_smtp
62
- end
63
- end
64
-
65
- # Attempts to send message using /usr/sbin/sendmail.
66
- # NOTE: sendmail ignores :from and :to options, using
67
- # From and To headers from the message
68
- def sendmail(options = nil)
69
- merge_options(options) if options
70
-
71
- prepare_email_message(options)
72
-
73
- IO.popen(@options[:sendmail_path] || "/usr/sbin/sendmail -t","w") do |pipe|
74
- pipe.puts(@options[:message])
75
- end
76
- end
77
-
78
- # Attempts to send message using Net::SMTP.
79
- def send_smtp(options = nil)
80
- merge_options(options) if options
94
+ def send(new_options = {})
95
+ merge_options(new_options)
96
+ merge_options(@_controller.mailer_override)
81
97
 
82
- prepare_email_message(options)
83
-
84
- require 'net/smtp' unless defined?(Net::SMTP)
85
- # begin
86
- Net::SMTP.start(
87
- @options[:server] || 'localhost',
88
- @options[:port] || 25,
89
- @options[:domain] || nil,
90
- @options[:account] || @options[:username] || nil,
91
- @options[:password] || nil,
92
- @options[:auth] || :plain
93
- ) do |smtp|
94
- smtp.sendmail(@options[:message], @options[:from], @options[:to])
95
- end
96
- # rescue
97
- # end
98
+ prepare_email_message
99
+ self.class.send(options)
98
100
  end
99
101
  end
100
102
  end
@@ -4,26 +4,50 @@ class Kiss
4
4
  # to cache database model classes, unless no model_dir is specified.
5
5
  class Model < Sequel::Model
6
6
  class << self
7
+ dsl_accessor :value_column, :display_column
8
+
7
9
  def set_dataset(source)
8
10
  super(source)
9
11
  end
10
12
 
13
+ def has_many(*args)
14
+ one_to_many(*args)
15
+ end
16
+
17
+ def belongs_to(*args)
18
+ many_to_one(*args)
19
+ end
20
+
21
+ def has_and_belongs_to_many(*args)
22
+ many_to_many(*args)
23
+ end
24
+
25
+ def alias_association(new_name, old_name)
26
+ alias_method new_name, old_name
27
+ alias_method :"#{new_name}_dataset", :"#{old_name}_dataset"
28
+ end
29
+ alias_method :alias_assoc, :alias_association
30
+
11
31
  # This method is called by Sequel::Model's association def methods.
12
32
  # Must return singularized table name for correct association key names.
13
33
  def name
14
- @table.to_s.singularize
34
+ @_table.to_s.singularize
15
35
  end
16
36
 
17
37
  def controller
18
38
  db.kiss_controller
19
39
  end
20
40
 
41
+ def request
42
+ db.kiss_request || controller
43
+ end
44
+
21
45
  def table
22
- @table.to_sym
46
+ @_table.to_sym
23
47
  end
24
48
 
25
49
  def table=(table)
26
- @table = table
50
+ @_table = table
27
51
  end
28
52
 
29
53
  # Name symbol for default foreign key
@@ -40,7 +64,7 @@ class Kiss
40
64
  opts = opts.clone
41
65
 
42
66
  unless opts[:class] || opts[:class_name]
43
- opts[:class_name] = name.to_s.singularize
67
+ opts[:class_name] = name.to_s.pluralize
44
68
  end
45
69
 
46
70
  super(type, name, opts, &block)
@@ -49,53 +73,222 @@ class Kiss
49
73
  end
50
74
 
51
75
  include Kiss::ControllerAccessors
76
+ include Kiss::RequestAccessors
77
+ alias_method :database, :db
78
+ end
79
+
80
+ def deferred_associations
81
+ @deferred_associations ||= {}
52
82
  end
53
83
 
54
- def method_missing(meth)
55
- raise NoMethodError, "undefined method `#{meth}' for database model `#{self.class.name}'"
84
+ def deferred_association_method_calls
85
+ @deferred_association_method_calls ||= []
86
+ end
87
+
88
+ def set_associated_object(opts, o)
89
+ if o && !o.pk
90
+ if (da = deferred_associations[opts[:name]]) != o
91
+ if da
92
+ da.deferred_association_method_calls.delete_if {|c| c[1] == [self, opts.setter_method, da] }
93
+ remove_deferred_reciprocal_object(opts, o)
94
+ end
95
+
96
+ deferred_associations[opts[:name]] = o
97
+ o.deferred_association_method_calls << [false, [self, opts.setter_method, o]]
98
+ add_deferred_reciprocal_object(opts, o)
99
+ end
100
+ o
101
+ else
102
+ deferred_associations.delete(opts[:name])
103
+ super
104
+ end
105
+ end
106
+
107
+ def add_associated_object(opts, o)
108
+ need_missing_opk = opts.need_associated_primary_key? && !o.pk
109
+ if !pk || need_missing_opk
110
+ double_defer = !pk && need_missing_opk
111
+ # TODO: add :uniq check here (and patch this in Sequel as well)
112
+ (deferred_associations[opts[:name]] ||= []).push(o)
113
+
114
+ method_call = [double_defer, [self, opts.add_method, o]]
115
+ deferred_association_method_calls << method_call unless pk
116
+ o.deferred_association_method_calls << method_call if need_missing_opk
117
+
118
+ add_deferred_reciprocal_object(opts, o)
119
+ o
120
+ else
121
+ deferred_associations[opts[:name]].delete(o) if deferred_associations[opts[:name]]
122
+ super
123
+ end
124
+ end
125
+
126
+ # Add/Set the current object to/as the given object's reciprocal association.
127
+ def add_deferred_reciprocal_object(opts, o)
128
+ return unless reciprocal = opts.reciprocal
129
+ if opts.reciprocal_array?
130
+ if array = o.deferred_associations[reciprocal] and !array.include?(self)
131
+ array.push(self)
132
+ end
133
+ else
134
+ o.deferred_associations[reciprocal] = self
135
+ end
136
+ end
137
+
138
+ def load_associated_objects(opts, reload=false)
139
+ if d = deferred_associations[opts[:name]]
140
+ opts.returns_array? ? super + d : d
141
+ else
142
+ super || set_associated_object(opts, opts.associated_class.new)
143
+ end
144
+ end
145
+
146
+ # Remove all associated objects from the given association
147
+ def remove_all_associated_objects(opts)
148
+ ret = super
149
+ if deferred_associations.include?(opts[:name])
150
+ def_ret = deferred_associations[opts[:name]].each do |o|
151
+ method_call = [self, opts.add_method, o]
152
+ deferred_association_method_calls.delete_if {|c| c[1] == method_call }
153
+ o.deferred_association_method_calls.delete_if {|c| c[1] == method_call }
154
+ remove_deferred_reciprocal_object(opts, o)
155
+ end
156
+ deferred_associations[opts[:name]] = []
157
+ (ret || []) + def_ret
158
+ else
159
+ ret
160
+ end
161
+ end
162
+
163
+ # Remove the given associated object from the given association
164
+ def remove_associated_object(opts, o)
165
+ if (array = deferred_associations[opts[:name]]) and array.delete(o)
166
+ method_call = [self, opts.add_method, o]
167
+ deferred_association_method_calls.delete_if {|c| c[1] == method_call }
168
+ o.deferred_association_method_calls.delete_if {|c| c[1] == method_call }
169
+ remove_deferred_reciprocal_object(opts, o)
170
+ else
171
+ super
172
+ end
173
+ o
174
+ end
175
+
176
+ # Remove/unset the current object from/as the given object's reciprocal association.
177
+ def remove_deferred_reciprocal_object(opts, o)
178
+ return unless reciprocal = opts.reciprocal
179
+ if opts.reciprocal_array?
180
+ if array = o.deferred_associations[reciprocal]
181
+ array.delete_if{|x| self === x}
182
+ end
183
+ else
184
+ o.deferred_associations[reciprocal] = nil
185
+ end
186
+ end
187
+
188
+ def after_create
189
+ deferred_association_method_calls.each do |call|
190
+ if call[0]
191
+ # call[0] (double_defer) is true
192
+ # now set it to false, do nothing else
193
+ # (defer again until other side is created)
194
+ call[0] = false
195
+ else
196
+ m = call[1]
197
+ m.shift.send(*m) if m
198
+ end
199
+ end
200
+ @deferred_association_method_calls = []
201
+ end
202
+
203
+ def method_missing(meth, *args, &block)
204
+ if meth.to_s =~ /(\w+)\?\Z/
205
+ column = $1.to_sym
206
+ if respond_to?(column)
207
+ val = self.send(column, *args, &block)
208
+ return !(val.nil? || val.zero?)
209
+ end
210
+ end
211
+ raise NoMethodError, "undefined method `#{meth}' for database model `#{self.class.name.pluralize}'"
56
212
  end
57
213
 
58
214
  def controller
59
215
  self.class.controller
60
216
  end
61
217
 
218
+ def request
219
+ self.class.request
220
+ end
221
+
222
+ def name
223
+ self[:name] || self.class.table.to_s.singularize.titleize
224
+ end
225
+
226
+ # Creates and invokes new Kiss::Mailer instance to send email message via SMTP.
227
+ def new_email(options = {})
228
+ request.new_email({
229
+ :data => self
230
+ }.merge(options))
231
+ end
232
+
233
+ def send_email(options = {})
234
+ new_email(options).send
235
+ end
236
+
237
+ def to_hash
238
+ result = {}
239
+ keys.each do |key|
240
+ result[key] = values[key]
241
+ end
242
+ result
243
+ end
244
+
62
245
  include Kiss::ControllerAccessors
246
+ include Kiss::RequestAccessors
247
+ alias_method :database, :db
63
248
  end
64
249
 
65
250
  class ModelCache
66
- def self.model_dir=(model_dir = nil)
67
- @@model_dir = model_dir && ::File.directory?(model_dir) ? model_dir : nil
68
- end
69
-
70
- def initialize(database = nil)
71
- @db = database
72
- @cache = {}
251
+ def initialize(database, model_dir)
252
+ @_db = database
253
+ @_model_dir = model_dir
254
+ @_cache = {}
255
+
256
+ # TODO: Fix this to use file cache and subclass hierarchy, so models can be
257
+ # reloaded if _model.rb changes.
258
+ @_root_class = Class.new(Kiss::Model)
259
+ if File.exist?(filename = "#{@_model_dir}/_model.rb")
260
+ @_root_class.class_eval(File.read(filename), filename)
261
+ end
73
262
  end
74
263
 
75
- def new_model_class(database,source)
76
- klass = Class.new(Kiss::Model)
264
+ def new_model_class(database, source)
265
+ klass = Class.new(@_root_class)
77
266
  klass.set_dataset(database[source])
78
267
  klass.table = source
268
+ klass.class_eval do
269
+ @_value_column = :id
270
+ @_display_column = :name
271
+ end
79
272
  klass
80
273
  end
81
274
 
82
275
  def [](source)
83
276
  raise 'argument to model cache must be symbol of database table name' unless source.is_a?(Symbol)
84
277
 
85
- database = @db
86
- @@model_dir ? begin
87
- # use file_cache
88
- model_path = "#{@@model_dir}/#{source}.rb"
89
- Kiss.file_cache(model_path) do |src|
90
- klass = new_model_class(database,source)
91
- klass.class_eval(src,model_path) if src
278
+ database = @_db
279
+ @_model_dir ? begin
280
+ # TODO: use request's file_cache
281
+ model_path = "#{@_model_dir}/#{source}.rb"
282
+ db.kiss_controller.file_cache(model_path) do |src|
283
+ klass = new_model_class(database, source)
284
+ klass.class_eval(src, model_path) if src
92
285
  klass
93
286
  end
94
- end : @cache[source] ||= new_model_class(database,source)
287
+ end : @_cache[source] ||= new_model_class(database, source)
95
288
  end
96
289
 
97
290
  def database
98
- @db
291
+ @_db
99
292
  end
100
293
  alias_method :db, :database
101
294
 
@@ -103,16 +296,12 @@ class Kiss
103
296
  Sequel::Model.dataset.literal(*args)
104
297
  end
105
298
  alias_method :quote, :literal
106
-
107
- def mdy_to_ymd(*args)
108
- Kiss.mdy_to_ymd(*args)
109
- end
110
299
  end
111
300
  end
112
301
 
113
302
  Sequel::Model::Associations::AssociationReflection.class_eval do
114
303
  def associated_class
115
- self[:class] ||= self[:model].controller.dbm[self[:class_name].to_s.pluralize.to_sym]
304
+ self[:class] ||= (self[:model].request || self[:model].controller).dbm[self[:class_name].to_s.pluralize.to_sym]
116
305
  end
117
306
  def default_left_key
118
307
  :"#{self[:model].name.singularize.underscore}_id"
@@ -131,7 +320,7 @@ class Date
131
320
  class << self
132
321
  alias_method :old_parse, :parse
133
322
  def parse(*args, &block)
134
- return SequelZeroTime.new(args[0]) if args[0] =~ /0000/
323
+ return SequelZeroTime.new(args[0]) if args[0] =~ /0000-00-00/
135
324
  old_parse(*args, &block)
136
325
  end
137
326
  end
@@ -184,12 +373,110 @@ end
184
373
  class BigDecimal
185
374
  # Formats number with comma-separated thousands.
186
375
  def format_thousands(value = to_f.to_s)
187
- integer, decimal = value.split(/\./,2)
188
- integer.reverse.gsub(/(\d{3})/,'\1,').sub(/\,(-?)$/,'\1').reverse + '.' + decimal
376
+ integer, decimal = value.split(/\./, 2)
377
+ integer.reverse.gsub(/(\d{3})/, '\1,').sub(/\,(-?)$/, '\1').reverse + '.' + decimal
378
+ end
379
+ end
380
+
381
+ class SequelZeroTime < String
382
+ def initialize(value = '0000-00-00 00:00', *args, &block)
383
+ super(value, *args, &block)
189
384
  end
190
385
 
191
- # Formats number to two decimal places.
192
- def format_currency
193
- format_thousands(sprintf("%0.2f",to_f))
386
+ # arithmetic operators
387
+ def +(*args)
388
+ self
389
+ end
390
+ def -(*args)
391
+ self
194
392
  end
195
- end
393
+
394
+ # comparision operators
395
+ def ==(value)
396
+ (value == 0 || value.is_a?(SequelZeroTime)) ? true : false
397
+ end
398
+ def >(value)
399
+ return false
400
+ end
401
+ def >=(value)
402
+ (value == 0 || value.is_a?(SequelZeroTime)) ? true : false
403
+ end
404
+ def <(value)
405
+ return true
406
+ end
407
+ def <=(value)
408
+ return true
409
+ end
410
+
411
+ # format conversion
412
+ def to_f; 0; end
413
+ def to_i; 0; end
414
+ def to_s; ''; end
415
+
416
+ # timezone conversion
417
+ def set_timezone!(zone, utc)
418
+ self
419
+ end
420
+ def from_timezone(zone)
421
+ self
422
+ end
423
+ def to_timezone(zone)
424
+ self
425
+ end
426
+ def to_utc
427
+ self
428
+ end
429
+ def zone
430
+ ''
431
+ end
432
+
433
+ # string representations
434
+ def strftime(*args); ''; end
435
+ def md; ''; end
436
+ def md_full; ''; end
437
+ alias_method :md_long, :md_full
438
+ def mdy; ''; end
439
+ def mdy_full; ''; end
440
+ alias_method :mdy_long, :mdy_full
441
+ def mdy_hm; ''; end
442
+ def mdy_hmz; ''; end
443
+ def mdy_hms; ''; end
444
+ def mdy_hmsz; ''; end
445
+ def ymd_hm; ''; end
446
+ def ymd_hmz; ''; end
447
+ def ymd_hms; ''; end
448
+ def ymd_hmsz; ''; end
449
+ def sql; '0000-00-00 00:00'; end
450
+ def mdy_hm_full; ''; end
451
+ alias_method :mdy_hm_long, :mdy_hm_full
452
+ def mdy_hmz_full; ''; end
453
+ alias_method :mdy_hmz_long, :mdy_hmz_full
454
+ def hm; ''; end
455
+ def hmz; ''; end
456
+ def hms; ''; end
457
+ def hmsz; ''; end
458
+ def hm_mdy; ''; end
459
+ def hmz_mdy; ''; end
460
+ def hms_mdy; ''; end
461
+ def hmsz_mdy; ''; end
462
+ def hm_mdy_full; ''; end
463
+ alias_method :hm_mdy_long, :hm_mdy_full
464
+ def hmz_mdy_full; ''; end
465
+ alias_method :hmz_mdy_long, :hmz_mdy_full
466
+
467
+ def zero?; true; end
468
+
469
+ def in_between?(*args); false; end
470
+ end
471
+
472
+ IrregularInflection = proc do
473
+ def self.irregular(singular, plural)
474
+ singular(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + singular[1..-1])
475
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
476
+
477
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
478
+ plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
479
+ end
480
+ end
481
+ Sequel::Inflections.class_eval &IrregularInflection
482
+ String::Inflections.class_eval &IrregularInflection