flex_date 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d8c5b1d1dd3cb06b8f3fc0c101dc59f00c5c809c
4
+ data.tar.gz: 6286c685d1e746ecd0c5259bd94ec3e657a7437d
5
+ SHA512:
6
+ metadata.gz: 1fcf79ccd39d5da8ffb6afc8bda0f1f6342d05f09e2f3e81cac52c275920a33ca93d09365f712e842601d6056d60b40ac2a0df18b7deecc8ec09aab31d48411b
7
+ data.tar.gz: 01bc8e5ae933677cb05ba694d61dbd175ae18016e22c2fa15da36d14447b36bd6083d3724fce061567823f16df8cdc0d5d10d6c84caba072b1f7467b81716d96
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,95 @@
1
+ = FlexDate
2
+
3
+ Flexible dates for Ruby.
4
+
5
+ <b>FlexDate is currently compatible with Rails 2.x and Rails 3.</b>
6
+
7
+
8
+ == The Problem
9
+
10
+ You are collecting dates of historic events but you don't always have <i>exact</i> dates. For example, you might know that an event occurred in March of 1862 but you don't know the exact day. You could store this as a Ruby Date object that looks like '1862-03-01' but, really, you don't want to specify a day at all. You want to <b>explicitly not store</b> the day part of the date.
11
+
12
+
13
+ == The Solution
14
+
15
+ FlexDate addresses this problem by extending Ruby's included Date class. At the moment the following methods are provided to create partial dates:
16
+
17
+ FlexDate.new(year = nil, month = nil, date = nil)
18
+ year=(int)
19
+ month=(int)
20
+ day=(int)
21
+
22
+ Where a full date (year, month, and day) is specified, a FlexDate behaves exactly like a normal Ruby Date. When a partial date is specified, only the following methods will work:
23
+
24
+ year
25
+ month
26
+ day
27
+ to_s
28
+ strftime
29
+
30
+ Comparison operators also (== and <=>) work and I hope to have date arithmetic operators (+ and -) up and running soon.
31
+
32
+
33
+ == Installation
34
+
35
+ To install FlexDate, use the standard Rails plugin installation script:
36
+
37
+ script/plugin install git://github.com/alexreisner/flex_date.git
38
+
39
+
40
+ == Integration with Rails/ActiveRecord
41
+
42
+ The MultipartDate module facilitates integration of FlexDate with ActiveRecord models. It requires that you store each date in three separate columns, each with a _y, _m, or _d suffix. So, if you have a Person model that stores birthdays, the columns required would be (all integers):
43
+
44
+ birthday_y
45
+ birthday_m
46
+ birthday_d
47
+
48
+ Then, simply put this in your Person model (you can list multiple attributes if needed):
49
+
50
+ multipart_date :birthday
51
+
52
+ and you'll be able to do things like this:
53
+
54
+ # Initialize a new Person object (don't know year they were born).
55
+ p = Person.new(:birthday_m => 8, :birthday_d => 23)
56
+
57
+ # See if their birthday is set.
58
+ p.birthday?
59
+ => true
60
+
61
+ # Get their birthday as a FlexDate object.
62
+ b = p.birthday
63
+ => #<FlexDate:0xb78b542c ...>
64
+
65
+ # Display their birthday nicely.
66
+ p.birthday.to_s(:long)
67
+ => "August 23"
68
+
69
+ # Add the year when you find out.
70
+ p.birthday_y = 1964
71
+ p.birthday.to_s(:long)
72
+ => "August 23, 1964"
73
+
74
+
75
+ I've also written another Rails plugin called Informant which provides a full-featured FormBuilder class, and includes a <tt>multipart_date_select</tt> field type which is a good compliment to MultipartDate in your Rails application. Install Informant from my Git repository:
76
+
77
+ script/plugin install git://github.com/alexreisner/informant.git
78
+
79
+
80
+ == Code Discussion
81
+
82
+ You might expect FlexDate to be a subclass of Date, but this is not the case. That implementation would be far more complicated because the instance methods of Date require certain instance variables to be set, and to meet certain requirements for validity. Massaging the Date class to accept an invalid date seems like the wrong approach for the modest goals of FlexDate, which is simply to allow storing of partial dates, and duplicate the behavior of the Date class <b>when a date is complete</b>.
83
+
84
+ So instead, FlexDate is a very simple class that inherits from nothing, provides some (hopefully) useful methods for partial dates, and uses <tt>method_missing</tt> to fall back to Date when a FlexDate object is complete (specifies year, month, and day).
85
+
86
+ Any comments or suggestions regarding this design are welcome.
87
+
88
+
89
+ == To-do List
90
+
91
+ * <tt>MultipartDate::multipart_date</tt> should also generate a setter method.
92
+ * Make date arithmetic work (Date minus FlexDate works, but neither FlexDate minus Date or FlexDate minus FlexDate works).
93
+
94
+
95
+ Copyright (c) 2008 Alex Reisner (alex@alexreisner.com), released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,84 @@
1
+ require 'rake'
2
+ require "rubygems"
3
+ require 'rake/testtask'
4
+ require 'rdoc/task'
5
+ require "rubygems/package_task"
6
+
7
+ desc 'Default: run unit tests.'
8
+ task :default => :test
9
+
10
+ require "rake/testtask"
11
+ desc 'Test the flex_date plugin.'
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << "test"
14
+ t.test_files = FileList["test/**/*_test.rb"]
15
+ t.verbose = true
16
+ end
17
+ task :default => ["test"]
18
+
19
+ # This builds the actual gem. For details of what all these options
20
+ # mean, and other ones you can add, check the documentation here:
21
+ #
22
+ # http://rubygems.org/read/chapter/20
23
+ #
24
+ spec = Gem::Specification.new do |s|
25
+
26
+ # Change these as appropriate
27
+ s.name = "flex_date"
28
+ s.version = "0.1.0"
29
+ s.summary = "Flexible dates for Ruby"
30
+ s.author = "Sean Gregory"
31
+ s.email = "seangregory@gmail.com"
32
+ s.homepage = "https://github.com/skinnyjames/flex_date"
33
+
34
+ s.has_rdoc = true
35
+ s.extra_rdoc_files = %w(README.rdoc)
36
+ s.rdoc_options = %w(--main README.rdoc)
37
+
38
+ # Add any extra files to include in the gem
39
+ s.files = %w(README.rdoc Rakefile MIT-LICENSE) + Dir.glob("{test,lib}/**/*")
40
+ s.require_paths = ["lib"]
41
+
42
+ # If you want to depend on other gems, add them here, along with any
43
+ # relevant versions
44
+ # s.add_dependency("some_other_gem", "~> 0.1.0")
45
+
46
+ # If your tests use any gems, include them here
47
+ # s.add_development_dependency("mocha") # for example
48
+ end
49
+
50
+ # This task actually builds the gem. We also regenerate a static
51
+ # .gemspec file, which is useful if something (i.e. GitHub) will
52
+ # be automatically building a gem for this project. If you're not
53
+ # using GitHub, edit as appropriate.
54
+ #
55
+ # To publish your gem online, install the 'gemcutter' gem; Read more
56
+ # about that here: http://gemcutter.org/pages/gem_docs
57
+ Gem::PackageTask.new(spec) do |pkg|
58
+ pkg.gem_spec = spec
59
+ end
60
+
61
+ desc "Build the gemspec file #{spec.name}.gemspec"
62
+ task :gemspec do
63
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
64
+ File.open(file, "w") {|f| f << spec.to_ruby }
65
+ end
66
+
67
+ # If you don't want to generate the .gemspec file, just remove this line. Reasons
68
+ # why you might want to generate a gemspec:
69
+ # - using bundler with a git source
70
+ # - building the gem without rake (i.e. gem build blah.gemspec)
71
+ # - maybe others?
72
+ task :package => :gemspec
73
+
74
+ # Generate documentation
75
+ RDoc::Task.new do |rd|
76
+ rd.main = "README.rdoc"
77
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
78
+ rd.rdoc_dir = "rdoc"
79
+ end
80
+
81
+ desc 'Clear out RDoc and generated packages'
82
+ task :clean => [:clobber_rdoc, :clobber_package] do
83
+ rm "#{spec.name}.gemspec"
84
+ end
data/lib/flex_date.rb ADDED
@@ -0,0 +1,120 @@
1
+ require 'date'
2
+ class FlexDate
3
+ include Comparable
4
+
5
+ def <=>(other)
6
+ spaceship = 0 # start assuming equal
7
+ %w(year month day).each do |p|
8
+ a = eval("self.#{p}") || 0
9
+ b = eval("other.#{p}") || 0
10
+ break if (spaceship = a <=> b) != 0
11
+ end
12
+ spaceship
13
+ end
14
+
15
+ def ==(other)
16
+ [year,month,day] == [other.year, other.month, other.day]
17
+ end
18
+
19
+ attr_accessor :year, :month, :day
20
+
21
+ ##
22
+ # Initialize the FlexDate with whichever are available: year, month, and day.
23
+ #
24
+ def initialize(year = nil, month = nil, day = nil)
25
+ @year, @month, @day = year, month, day
26
+ end
27
+
28
+ ##
29
+ # Define some preset date formats.
30
+ #
31
+ DATE_FORMATS = {
32
+ :short => "%b %e",
33
+ :medium => "%e %b %Y",
34
+ :long => "%B %e, %Y",
35
+ :db => "%Y-%m-%d",
36
+ :number => "%Y%m%d",
37
+ :rfc822 => "%e %b %Y"
38
+ }
39
+
40
+ ##
41
+ # String representation of a date. Pass a <tt>strftime</tt>-format string
42
+ # or a symbol which is a key in the DATE_FORMATS hash.
43
+ #
44
+ def to_s(format = :medium)
45
+ format = DATE_FORMATS[format] if format.is_a?(Symbol)
46
+ s = strftime(format)
47
+
48
+ # Do some rough cleanup (this isn't very "programmatic").
49
+ replacements = [
50
+ [/^[ ,]+/, ''], # leading whitespace and commas
51
+ [/[ ,]+$/, ''], # trailing whitespace and commas
52
+ [/ +, +/, ' '], # commas and whitespace in middle
53
+ ]
54
+ s.strip!
55
+ replacements.each do |r|
56
+ s.gsub!(r[0], r[1])
57
+ end
58
+ s
59
+ end
60
+
61
+ ##
62
+ # Does this FlexDate specify a year, month, and day?
63
+ #
64
+ def complete?
65
+ not (@year and @month and @day).nil?
66
+ end
67
+
68
+ ##
69
+ # Same as Date#strftime but gracefully removes missing parts.
70
+ #
71
+ def strftime(format = '%F')
72
+ # If we have a complete date, just let Date handle it.
73
+ return real_date.strftime(format) if complete?
74
+
75
+ # If we have a partial date, define token dependencies.
76
+ dependencies = {
77
+ :year => %w(C c D F G g u V v w x Y y),
78
+ :month => %w(B b c D F h m u V v w x),
79
+ :day => %w(A a c D d e F j u V v w x),
80
+ :time => %w(H I k L M N P p Q R r S s T X Z)
81
+ }
82
+
83
+ # Remove tokens that refer to missing parts.
84
+ format = format.gsub(/%([-_0^#]+)?(\d+)?[EO]?(:{1,3}z|.)/m) do |m|
85
+ s, w, c = $1, $2, $3
86
+ ok = true
87
+ dependencies.each do |attr,tokens|
88
+ missing = instance_variable_get("@#{attr}").nil?
89
+ ok = false if (tokens.include?(c) and missing)
90
+ end
91
+ ok ? m : ''
92
+ end
93
+ phony_date.strftime(format)
94
+ end
95
+
96
+ ##
97
+ # Pass unimplemented methods along to a regular Date object if possible.
98
+ #
99
+ def method_missing(name, *args)
100
+ real_date.send(name, *args) if complete?
101
+ end
102
+
103
+
104
+ private # -------------------------------------------------------------------
105
+
106
+ ##
107
+ # Get a Date object based on the current values of @year, @month, and @day.
108
+ # Returns nil unless all three are known.
109
+ #
110
+ def real_date
111
+ Date.new(@year, @month, @day) if complete?
112
+ end
113
+
114
+ ##
115
+ # Get a Date object with "1" set for any unknown parts.
116
+ #
117
+ def phony_date
118
+ Date.new((@year || 1), (@month || 1), (@day || 1))
119
+ end
120
+ end
@@ -0,0 +1,62 @@
1
+ require 'test/unit'
2
+ $:.unshift File.join(File.dirname(__FILE__), '../lib')
3
+ require 'flex_date'
4
+
5
+ class FlexDateTest < Test::Unit::TestCase
6
+
7
+ def test_creation
8
+ assert FlexDate.new(1953, 11, 2).is_a?(FlexDate)
9
+ assert FlexDate.new(1953, 11).is_a?(FlexDate)
10
+ assert FlexDate.new(1953).is_a?(FlexDate)
11
+ assert FlexDate.new(nil, 11).is_a?(FlexDate)
12
+ assert FlexDate.new(nil, 11, 2).is_a?(FlexDate)
13
+ assert FlexDate.new(nil, nil, 2).is_a?(FlexDate)
14
+ assert FlexDate.new.is_a?(FlexDate)
15
+ end
16
+
17
+ def test_accessors
18
+ f = FlexDate.new
19
+
20
+ assert !f.complete?
21
+ assert f.year.nil?
22
+ f.year = 1794
23
+ assert_equal 1794, f.year
24
+
25
+ assert !f.complete?
26
+ assert f.month.nil?
27
+ f.month = 9
28
+ assert_equal 9, f.month
29
+
30
+ assert !f.complete?
31
+ assert f.day.nil?
32
+ f.day = 4
33
+ assert_equal 4, f.day
34
+
35
+ assert f.complete?
36
+ end
37
+
38
+ def test_formatting
39
+ f = FlexDate.new
40
+ assert_equal "", f.strftime("%Y")
41
+ f.year = 2087
42
+ assert_equal "2087", f.strftime("%Y")
43
+ assert_equal "2087-", f.strftime("%Y-%m")
44
+ assert_equal "2087", f.to_s("%Y")
45
+ assert_equal "2087", f.to_s(:long)
46
+ f.month = 8
47
+ assert_equal "Aug 2087", f.to_s("%b %e, %Y")
48
+ end
49
+
50
+ def test_comparison_and_equality
51
+ a = FlexDate.new(2008,6,4)
52
+ b = FlexDate.new(2004,9,30)
53
+ assert_equal 0, a <=> a
54
+ assert a == a
55
+ assert a != b
56
+ assert_equal 1, a <=> b
57
+ c = FlexDate.new(2004)
58
+ d = FlexDate.new(1980,8,3)
59
+ e = FlexDate.new(1945,6)
60
+ assert_equal [e,d,c,b,a], [b,e,a,d,c].sort
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flex_date
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Gregory
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-06-18 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: seangregory@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files:
18
+ - README.rdoc
19
+ files:
20
+ - MIT-LICENSE
21
+ - README.rdoc
22
+ - Rakefile
23
+ - lib/flex_date.rb
24
+ - test/flex_date_test.rb
25
+ homepage: https://github.com/skinnyjames/flex_date
26
+ licenses: []
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options:
30
+ - "--main"
31
+ - README.rdoc
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 2.5.2
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: Flexible dates for Ruby
50
+ test_files: []