jc-validates_timeliness 3.1.0

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