jc-validates_timeliness 3.1.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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +14 -0
  3. data/CHANGELOG.rdoc +183 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +301 -0
  6. data/Rakefile +30 -0
  7. data/gemfiles/mongoid_2_1.gemfile +16 -0
  8. data/gemfiles/mongoid_2_2.gemfile +16 -0
  9. data/gemfiles/mongoid_2_3.gemfile +16 -0
  10. data/gemfiles/mongoid_2_4.gemfile +16 -0
  11. data/gemfiles/rails_3_0.gemfile +15 -0
  12. data/gemfiles/rails_3_1.gemfile +15 -0
  13. data/gemfiles/rails_3_2.gemfile +15 -0
  14. data/init.rb +1 -0
  15. data/lib/generators/validates_timeliness/install_generator.rb +16 -0
  16. data/lib/generators/validates_timeliness/templates/en.yml +16 -0
  17. data/lib/generators/validates_timeliness/templates/validates_timeliness.rb +40 -0
  18. data/lib/validates_timeliness.rb +70 -0
  19. data/lib/validates_timeliness/attribute_methods.rb +92 -0
  20. data/lib/validates_timeliness/conversion.rb +70 -0
  21. data/lib/validates_timeliness/extensions.rb +14 -0
  22. data/lib/validates_timeliness/extensions/date_time_select.rb +61 -0
  23. data/lib/validates_timeliness/extensions/multiparameter_handler.rb +80 -0
  24. data/lib/validates_timeliness/helper_methods.rb +23 -0
  25. data/lib/validates_timeliness/orm/active_record.rb +53 -0
  26. data/lib/validates_timeliness/orm/mongoid.rb +63 -0
  27. data/lib/validates_timeliness/railtie.rb +15 -0
  28. data/lib/validates_timeliness/validator.rb +117 -0
  29. data/lib/validates_timeliness/version.rb +3 -0
  30. data/spec/spec_helper.rb +100 -0
  31. data/spec/support/config_helper.rb +36 -0
  32. data/spec/support/model_helpers.rb +27 -0
  33. data/spec/support/tag_matcher.rb +35 -0
  34. data/spec/support/test_model.rb +60 -0
  35. data/spec/validates_timeliness/attribute_methods_spec.rb +86 -0
  36. data/spec/validates_timeliness/conversion_spec.rb +234 -0
  37. data/spec/validates_timeliness/extensions/date_time_select_spec.rb +163 -0
  38. data/spec/validates_timeliness/extensions/multiparameter_handler_spec.rb +44 -0
  39. data/spec/validates_timeliness/helper_methods_spec.rb +30 -0
  40. data/spec/validates_timeliness/orm/active_record_spec.rb +244 -0
  41. data/spec/validates_timeliness/orm/mongoid_spec.rb +189 -0
  42. data/spec/validates_timeliness/validator/after_spec.rb +57 -0
  43. data/spec/validates_timeliness/validator/before_spec.rb +57 -0
  44. data/spec/validates_timeliness/validator/is_at_spec.rb +61 -0
  45. data/spec/validates_timeliness/validator/on_or_after_spec.rb +57 -0
  46. data/spec/validates_timeliness/validator/on_or_before_spec.rb +57 -0
  47. data/spec/validates_timeliness/validator_spec.rb +246 -0
  48. data/spec/validates_timeliness_spec.rb +43 -0
  49. data/validates_timeliness.gemspec +20 -0
  50. metadata +128 -0
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require 'bundler'
2
+ require 'bundler/setup'
3
+
4
+ require 'appraisal'
5
+
6
+ Bundler::GemHelper.install_tasks
7
+
8
+ require 'rdoc/task'
9
+ require 'rspec/core/rake_task'
10
+
11
+ desc "Run specs"
12
+ RSpec::Core::RakeTask.new(:spec)
13
+
14
+ desc "Generate code coverage"
15
+ RSpec::Core::RakeTask.new(:coverage) do |t|
16
+ t.rcov = true
17
+ t.rcov_opts = ['--exclude', 'spec']
18
+ end
19
+
20
+ desc 'Generate documentation for plugin.'
21
+ Rake::RDocTask.new(:rdoc) do |rdoc|
22
+ rdoc.rdoc_dir = 'rdoc'
23
+ rdoc.title = 'ValidatesTimeliness'
24
+ rdoc.options << '--line-numbers' << '--inline-source'
25
+ rdoc.rdoc_files.include('README')
26
+ rdoc.rdoc_files.include('lib/**/*.rb')
27
+ end
28
+
29
+ desc 'Default: run specs.'
30
+ task :default => :spec
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 3.2.6"
6
+ gem "rspec", "~> 2.8"
7
+ gem "rspec-rails", "~> 2.8"
8
+ gem "timecop"
9
+ gem "rspec_tag_matchers"
10
+ gem "ruby-debug", :platforms=>[:ruby_18, :jruby]
11
+ gem "debugger", :platforms=>[:ruby_19]
12
+ gem "appraisal"
13
+ gem "sqlite3"
14
+ gem "mongoid", "~> 2.1.0"
15
+
16
+ gemspec :path=>"../"
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 3.2.6"
6
+ gem "rspec", "~> 2.8"
7
+ gem "rspec-rails", "~> 2.8"
8
+ gem "timecop"
9
+ gem "rspec_tag_matchers"
10
+ gem "ruby-debug", :platforms=>[:ruby_18, :jruby]
11
+ gem "debugger", :platforms=>[:ruby_19]
12
+ gem "appraisal"
13
+ gem "sqlite3"
14
+ gem "mongoid", "~> 2.2.0"
15
+
16
+ gemspec :path=>"../"
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 3.2.6"
6
+ gem "rspec", "~> 2.8"
7
+ gem "rspec-rails", "~> 2.8"
8
+ gem "timecop"
9
+ gem "rspec_tag_matchers"
10
+ gem "ruby-debug", :platforms=>[:ruby_18, :jruby]
11
+ gem "debugger", :platforms=>[:ruby_19]
12
+ gem "appraisal"
13
+ gem "sqlite3"
14
+ gem "mongoid", "~> 2.3.0"
15
+
16
+ gemspec :path=>"../"
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 3.2.6"
6
+ gem "rspec", "~> 2.8"
7
+ gem "rspec-rails", "~> 2.8"
8
+ gem "timecop"
9
+ gem "rspec_tag_matchers"
10
+ gem "ruby-debug", :platforms=>[:ruby_18, :jruby]
11
+ gem "debugger", :platforms=>[:ruby_19]
12
+ gem "appraisal"
13
+ gem "sqlite3"
14
+ gem "mongoid", "~> 2.4.0"
15
+
16
+ gemspec :path=>"../"
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rspec", "~> 2.8"
6
+ gem "rspec-rails", "~> 2.8"
7
+ gem "timecop"
8
+ gem "rspec_tag_matchers"
9
+ gem "ruby-debug", :platforms=>[:ruby_18, :jruby]
10
+ gem "debugger", :platforms=>[:ruby_19]
11
+ gem "appraisal"
12
+ gem "sqlite3"
13
+ gem "rails", "~> 3.0.0"
14
+
15
+ gemspec :path=>"../"
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rspec", "~> 2.8"
6
+ gem "rspec-rails", "~> 2.8"
7
+ gem "timecop"
8
+ gem "rspec_tag_matchers"
9
+ gem "ruby-debug", :platforms=>[:ruby_18, :jruby]
10
+ gem "debugger", :platforms=>[:ruby_19]
11
+ gem "appraisal"
12
+ gem "sqlite3"
13
+ gem "rails", "~> 3.1.0"
14
+
15
+ gemspec :path=>"../"
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rspec", "~> 2.8"
6
+ gem "rspec-rails", "~> 2.8"
7
+ gem "timecop"
8
+ gem "rspec_tag_matchers"
9
+ gem "ruby-debug", :platforms=>[:ruby_18, :jruby]
10
+ gem "debugger", :platforms=>[:ruby_19]
11
+ gem "appraisal"
12
+ gem "sqlite3"
13
+ gem "rails", "~> 3.2.0"
14
+
15
+ gemspec :path=>"../"
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'validates_timeliness'
@@ -0,0 +1,16 @@
1
+ module ValidatesTimeliness
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ desc "Copy ValidatesTimeliness default files"
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ def copy_initializers
8
+ copy_file 'validates_timeliness.rb', 'config/initializers/validates_timeliness.rb'
9
+ end
10
+
11
+ def copy_locale_file
12
+ copy_file 'en.yml', 'config/locales/validates_timeliness.en.yml'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ en:
2
+ errors:
3
+ messages:
4
+ invalid_date: "is not a valid date"
5
+ invalid_time: "is not a valid time"
6
+ invalid_datetime: "is not a valid datetime"
7
+ is_at: "must be at %{restriction}"
8
+ before: "must be before %{restriction}"
9
+ on_or_before: "must be on or before %{restriction}"
10
+ after: "must be after %{restriction}"
11
+ on_or_after: "must be on or after %{restriction}"
12
+ validates_timeliness:
13
+ error_value_formats:
14
+ date: '%Y-%m-%d'
15
+ time: '%H:%M:%S'
16
+ datetime: '%Y-%m-%d %H:%M:%S'
@@ -0,0 +1,40 @@
1
+ ValidatesTimeliness.setup do |config|
2
+ # Extend ORM/ODMs for full support (:active_record, :mongoid).
3
+ # config.extend_orms = [ :active_record ]
4
+ #
5
+ # Default timezone
6
+ # config.default_timezone = :utc
7
+ #
8
+ # Set the dummy date part for a time type values.
9
+ # config.dummy_date_for_time_type = [ 2000, 1, 1 ]
10
+ #
11
+ # Ignore errors when restriction options are evaluated
12
+ # config.ignore_restriction_errors = false
13
+ #
14
+ # Re-display invalid values in date/time selects
15
+ # config.enable_date_time_select_extension!
16
+ #
17
+ # Handle multiparameter date/time values strictly
18
+ # config.enable_multiparameter_extension!
19
+ #
20
+ # Shorthand date and time symbols for restrictions
21
+ # config.restriction_shorthand_symbols.update(
22
+ # :now => lambda { Time.current },
23
+ # :today => lambda { Date.current }
24
+ # )
25
+ #
26
+ # Use the plugin date/time parser which is stricter and extendable
27
+ # config.use_plugin_parser = false
28
+ #
29
+ # Add one or more formats making them valid. e.g. add_formats(:date, 'd(st|rd|th) of mmm, yyyy')
30
+ # config.parser.add_formats()
31
+ #
32
+ # Remove one or more formats making them invalid. e.g. remove_formats(:date, 'dd/mm/yyy')
33
+ # config.parser.remove_formats()
34
+ #
35
+ # Change the amiguous year threshold when parsing a 2 digit year
36
+ # config.parser.ambiguous_year_threshold = 30
37
+ #
38
+ # Treat ambiguous dates, such as 01/02/1950, as a Non-US date.
39
+ # config.parser.remove_us_formats
40
+ end
@@ -0,0 +1,70 @@
1
+ require 'date'
2
+ require 'active_support/concern'
3
+ require 'active_support/core_ext/module'
4
+ require 'active_support/core_ext/hash/except'
5
+ require 'active_support/core_ext/string/conversions'
6
+ require 'active_support/core_ext/date/acts_like'
7
+ require 'active_support/core_ext/date/conversions'
8
+ require 'active_support/core_ext/time/acts_like'
9
+ require 'active_support/core_ext/time/conversions'
10
+ require 'active_support/core_ext/date_time/acts_like'
11
+ require 'active_support/core_ext/date_time/conversions'
12
+ require 'timeliness'
13
+
14
+ Timeliness.module_eval do
15
+ class << self
16
+ alias :dummy_date_for_time_type :date_for_time_type
17
+ alias :dummy_date_for_time_type= :date_for_time_type=
18
+ alias :remove_us_formats :use_euro_formats
19
+ end
20
+ end
21
+
22
+ module ValidatesTimeliness
23
+ autoload :VERSION, 'validates_timeliness/version'
24
+
25
+ class << self
26
+ delegate :default_timezone, :default_timezone=, :dummy_date_for_time_type, :dummy_date_for_time_type=, :to => Timeliness
27
+
28
+ attr_accessor :extend_orms, :ignore_restriction_errors, :restriction_shorthand_symbols, :use_plugin_parser
29
+ end
30
+
31
+ # Extend ORM/ODMs for full support (:active_record, :mongoid).
32
+ self.extend_orms = []
33
+
34
+ # Ignore errors when restriction options are evaluated
35
+ self.ignore_restriction_errors = false
36
+
37
+ # Shorthand time and date symbols for restrictions
38
+ self.restriction_shorthand_symbols = {
39
+ :now => lambda { Time.current },
40
+ :today => lambda { Date.current }
41
+ }
42
+
43
+ # Use the plugin date/time parser which is stricter and extensible
44
+ self.use_plugin_parser = false
45
+
46
+ # Default timezone
47
+ self.default_timezone = :utc
48
+
49
+ # Set the dummy date part for a time type values.
50
+ self.dummy_date_for_time_type = [ 2000, 1, 1 ]
51
+
52
+ # Setup method for plugin configuration
53
+ def self.setup
54
+ yield self
55
+ load_orms
56
+ end
57
+
58
+ def self.load_orms
59
+ extend_orms.each {|orm| require "validates_timeliness/orm/#{orm}" }
60
+ end
61
+
62
+ def self.parser; Timeliness end
63
+ end
64
+
65
+ require 'validates_timeliness/conversion'
66
+ require 'validates_timeliness/validator'
67
+ require 'validates_timeliness/helper_methods'
68
+ require 'validates_timeliness/attribute_methods'
69
+ require 'validates_timeliness/extensions'
70
+ require 'validates_timeliness/railtie' if defined?(Rails)
@@ -0,0 +1,92 @@
1
+ module ValidatesTimeliness
2
+ module AttributeMethods
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :timeliness_validated_attributes
7
+ self.timeliness_validated_attributes = []
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ public
13
+ # Override in ORM shim
14
+ def timeliness_attribute_timezone_aware?(attr_name)
15
+ false
16
+ end
17
+
18
+ # Override in ORM shim
19
+ def timeliness_attribute_type(attr_name)
20
+ :datetime
21
+ end
22
+
23
+ def undefine_attribute_methods
24
+ super
25
+ undefine_timeliness_attribute_methods
26
+ end
27
+
28
+ protected
29
+
30
+ def define_timeliness_methods(before_type_cast=false)
31
+ return if timeliness_validated_attributes.blank?
32
+ timeliness_validated_attributes.each do |attr_name|
33
+ define_attribute_timeliness_methods(attr_name, before_type_cast)
34
+ end
35
+ end
36
+
37
+ def define_attribute_timeliness_methods(attr_name, before_type_cast=false)
38
+ define_timeliness_write_method(attr_name)
39
+ define_timeliness_before_type_cast_method(attr_name) if before_type_cast
40
+ end
41
+
42
+ def define_timeliness_write_method(attr_name)
43
+ method_body, line = <<-EOV, __LINE__ + 1
44
+ def #{attr_name}=(value)
45
+ original_value = value
46
+ @timeliness_cache ||= {}
47
+ @timeliness_cache["#{attr_name}"] = original_value
48
+ #{ "if value.is_a?(String)\n#{timeliness_type_cast_code(attr_name, 'value')}\nend" if ValidatesTimeliness.use_plugin_parser }
49
+
50
+ super(value)
51
+ end
52
+ EOV
53
+ generated_timeliness_methods.module_eval(method_body, __FILE__, line)
54
+ end
55
+
56
+ def define_timeliness_before_type_cast_method(attr_name)
57
+ method_body, line = <<-EOV, __LINE__ + 1
58
+ def #{attr_name}_before_type_cast
59
+ _timeliness_raw_value_for('#{attr_name}') || @attributes['#{attr_name}']
60
+ end
61
+ EOV
62
+ generated_timeliness_methods.module_eval(method_body, __FILE__, line)
63
+ end
64
+
65
+ def timeliness_type_cast_code(attr_name, var_name)
66
+ type = timeliness_attribute_type(attr_name)
67
+ timezone_aware = timeliness_attribute_timezone_aware?(attr_name)
68
+ timezone = :current if timezone_aware
69
+
70
+ "#{var_name} = Timeliness::Parser.parse(#{var_name}, :#{type}, :zone => #{timezone.inspect})"
71
+ end
72
+
73
+ def generated_timeliness_methods
74
+ @generated_timeliness_methods ||= Module.new.tap { |m| include(m) }
75
+ end
76
+
77
+ def undefine_timeliness_attribute_methods
78
+ generated_timeliness_methods.module_eval do
79
+ instance_methods.each { |m| undef_method(m) }
80
+ end
81
+ end
82
+ end
83
+
84
+ def _timeliness_raw_value_for(attr_name)
85
+ @timeliness_cache && @timeliness_cache[attr_name]
86
+ end
87
+
88
+ def _clear_timeliness_cache
89
+ @timeliness_cache = {}
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,70 @@
1
+ module ValidatesTimeliness
2
+ module Conversion
3
+
4
+ def type_cast_value(value, type)
5
+ return nil if value.nil? || !value.respond_to?(:to_time)
6
+
7
+ value = value.in_time_zone if value.acts_like?(:time) && @timezone_aware
8
+ value = case type
9
+ when :time
10
+ dummy_time(value)
11
+ when :date
12
+ value.to_date
13
+ when :datetime
14
+ value.is_a?(Time) ? value : value.to_time
15
+ end
16
+ if options[:ignore_usec] && value.is_a?(Time)
17
+ Timeliness::Parser.make_time(Array(value).reverse[4..9], (:current if @timezone_aware))
18
+ else
19
+ value
20
+ end
21
+ end
22
+
23
+ def dummy_time(value)
24
+ time = if value.acts_like?(:time)
25
+ value = value.in_time_zone if @timezone_aware
26
+ [value.hour, value.min, value.sec]
27
+ else
28
+ [0,0,0]
29
+ end
30
+ values = ValidatesTimeliness.dummy_date_for_time_type + time
31
+ Timeliness::Parser.make_time(values, (:current if @timezone_aware))
32
+ end
33
+
34
+ def evaluate_option_value(value, record)
35
+ case value
36
+ when Time, Date
37
+ value
38
+ when String
39
+ parse(value)
40
+ when Symbol
41
+ if !record.respond_to?(value) && restriction_shorthand?(value)
42
+ ValidatesTimeliness.restriction_shorthand_symbols[value].call
43
+ else
44
+ evaluate_option_value(record.send(value), record)
45
+ end
46
+ when Proc
47
+ result = value.arity > 0 ? value.call(record) : value.call
48
+ evaluate_option_value(result, record)
49
+ else
50
+ value
51
+ end
52
+ end
53
+
54
+ def restriction_shorthand?(symbol)
55
+ ValidatesTimeliness.restriction_shorthand_symbols.keys.include?(symbol)
56
+ end
57
+
58
+ def parse(value)
59
+ return nil if value.nil?
60
+ if ValidatesTimeliness.use_plugin_parser
61
+ Timeliness::Parser.parse(value, @type, :zone => (:current if @timezone_aware), :format => options[:format], :strict => false)
62
+ else
63
+ @timezone_aware ? Time.zone.parse(value) : value.to_time(ValidatesTimeliness.default_timezone)
64
+ end
65
+ rescue ArgumentError, TypeError
66
+ nil
67
+ end
68
+
69
+ end
70
+ end