timely 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +14 -0
  5. data/CHANGELOG.md +5 -0
  6. data/Gemfile +1 -12
  7. data/README.md +5 -0
  8. data/Rakefile +1 -139
  9. data/gemfiles/rails3.gemfile +8 -0
  10. data/gemfiles/rails4.gemfile +9 -0
  11. data/lib/timely.rb +7 -3
  12. data/lib/timely/date.rb +20 -0
  13. data/lib/timely/date_chooser.rb +10 -5
  14. data/lib/timely/date_range.rb +47 -10
  15. data/lib/timely/rails.rb +10 -3
  16. data/lib/timely/rails/calendar_tag.rb +52 -0
  17. data/lib/timely/rails/date.rb +5 -0
  18. data/lib/timely/rails/date_group.rb +68 -99
  19. data/lib/timely/rails/date_range_validity_module.rb +27 -0
  20. data/lib/timely/rails/date_time.rb +20 -0
  21. data/lib/timely/rails/extensions.rb +23 -11
  22. data/lib/timely/rails/period.rb +31 -0
  23. data/lib/timely/rails/season.rb +65 -75
  24. data/lib/timely/railtie.rb +7 -0
  25. data/lib/timely/temporal_patterns/finder.rb +152 -0
  26. data/lib/timely/temporal_patterns/frequency.rb +108 -0
  27. data/lib/timely/temporal_patterns/interval.rb +67 -0
  28. data/lib/timely/temporal_patterns/pattern.rb +160 -0
  29. data/lib/timely/time_since.rb +17 -0
  30. data/lib/timely/version.rb +3 -0
  31. data/spec/calendar_tag_spec.rb +29 -0
  32. data/spec/date_chooser_spec.rb +36 -27
  33. data/spec/date_group_spec.rb +9 -9
  34. data/spec/date_range_spec.rb +58 -20
  35. data/spec/date_spec.rb +20 -12
  36. data/spec/extensions_spec.rb +32 -0
  37. data/spec/rails/date_spec.rb +16 -0
  38. data/spec/rails/date_time_spec.rb +20 -0
  39. data/spec/rails/period_spec.rb +17 -0
  40. data/spec/schema.rb +5 -0
  41. data/spec/season_spec.rb +21 -24
  42. data/spec/spec_helper.rb +5 -20
  43. data/spec/string_spec.rb +4 -3
  44. data/spec/support/coverage.rb +26 -0
  45. data/spec/temporal_patterns_spec.rb +28 -0
  46. data/spec/time_since_spec.rb +24 -0
  47. data/spec/time_spec.rb +14 -14
  48. data/spec/trackable_date_set_spec.rb +14 -14
  49. data/spec/week_days_spec.rb +18 -18
  50. data/timely.gemspec +34 -98
  51. metadata +244 -21
  52. data/lib/timely/temporal_patterns.rb +0 -441
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2cebe03ec2467f4efe23f881f70be2c08fbb7e20
4
+ data.tar.gz: 355bf2273646023df6e0f7e9218e7a91390dd25a
5
+ SHA512:
6
+ metadata.gz: bec47226548cc65af0aa7e0da099b5dc4f4b80056eb1f47e0f5de53959db654b18ed851245fbab86f9d45a0d80cb3fcc3a0bbcc9cc6188958d120402832b78f1
7
+ data.tar.gz: c705e73fe8447761b8ea7a79984a77aeb13687bb81bc354e53f5f434d64f9d70f51325da10f81df7811e3c02a18621e02cc5b6dbcf7defcb33aa6f944ea066fe
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg
2
+ coverage
3
+ Gemfile.lock
4
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2
4
+ script: bundle exec rake spec
5
+ gemfile:
6
+ - gemfiles/rails3.gemfile
7
+ - gemfiles/rails4.gemfile
8
+ notifications:
9
+ email:
10
+ - support@travellink.com.au
11
+ flowdock:
12
+ secure: QC1mV/0NZwA4tU2K1BQSety/seEf7ujL3uUlddXrMm0yHp0NBMlo1tnQ7KKopSKoyoPj9M9KmfhrCdbdZXDOU57WucH+pz7Tn9Zp9VkPUMniN4rYSwkrF17vihM5NpdyDzqo5mzthVPUEV0DtlK+Uu2W4wMUpu1uKaahFzHJH6k=
13
+ sudo: false
14
+ cache: bundler
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ * Initial release
data/Gemfile CHANGED
@@ -1,13 +1,2 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
  gemspec
3
-
4
- group :development, :test do
5
- gem 'rake', '~> 0.9.2'
6
- gem 'rdoc', '~> 3.12'
7
- gem 'rspec'
8
- gem 'simplecov-rcov'
9
- gem 'simplecov'
10
- gem 'sqlite3'
11
- gem 'activesupport', '~> 2.3.0'
12
- gem 'activerecord', '~> 2.3.0'
13
- end
data/README.md CHANGED
@@ -1,6 +1,11 @@
1
1
  Timely
2
2
  ======
3
3
 
4
+ [![Build Status](https://travis-ci.org/sealink/timely.png?branch=master)](https://travis-ci.org/sealink/timely)
5
+ [![Coverage Status](https://coveralls.io/repos/sealink/timely/badge.png)](https://coveralls.io/r/sealink/timely)
6
+ [![Dependency Status](https://gemnasium.com/sealink/timely.png?travis)](https://gemnasium.com/sealink/timely)
7
+ [![Code Climate](https://codeclimate.com/github/sealink/timely.png)](https://codeclimate.com/github/sealink/timely)
8
+
4
9
  # DESCRIPTION
5
10
 
6
11
  Various helpers to work with times, dates and weekdays, etc.
data/Rakefile CHANGED
@@ -1,47 +1,4 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'date'
4
-
5
- #############################################################################
6
- #
7
- # Helper functions
8
- #
9
- #############################################################################
10
-
11
- def name
12
- @name ||= Dir['*.gemspec'].first.split('.').first
13
- end
14
-
15
- def version
16
- line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
17
- line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
18
- end
19
-
20
- def date
21
- Date.today.to_s
22
- end
23
-
24
- def rubyforge_project
25
- name
26
- end
27
-
28
- def gemspec_file
29
- "#{name}.gemspec"
30
- end
31
-
32
- def gem_file
33
- "#{name}-#{version}.gem"
34
- end
35
-
36
- def replace_header(head, header_name)
37
- head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
38
- end
39
-
40
- #############################################################################
41
- #
42
- # Standard tasks
43
- #
44
- #############################################################################
1
+ require "bundler/gem_tasks"
45
2
 
46
3
  desc 'Default: run specs.'
47
4
  task :default => :spec
@@ -53,98 +10,3 @@ RSpec::Core::RakeTask.new do |t|
53
10
  t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
54
11
  # Put spec opts in a file named .rspec in root
55
12
  end
56
-
57
- desc "Generate SimpleCov test coverage and open in your browser"
58
- task :coverage do
59
- ENV['COVERAGE'] = 'true'
60
- Rake::Task['spec'].invoke
61
- end
62
-
63
- require 'rdoc/task'
64
- RDoc::Task.new do |rdoc|
65
- rdoc.rdoc_dir = 'rdoc'
66
- rdoc.title = "#{name} #{version}"
67
- rdoc.rdoc_files.include('README*')
68
- rdoc.rdoc_files.include('lib/**/*.rb')
69
- end
70
-
71
- desc "Open an irb session preloaded with this library"
72
- task :console do
73
- sh "irb -rubygems -r ./lib/#{name}.rb"
74
- end
75
-
76
- #############################################################################
77
- #
78
- # Custom tasks (add your own tasks here)
79
- #
80
- #############################################################################
81
-
82
-
83
-
84
- #############################################################################
85
- #
86
- # Packaging tasks
87
- #
88
- #############################################################################
89
-
90
- desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
91
- task :release => :build do
92
- unless `git branch` =~ /^\* master$/
93
- puts "You must be on the master branch to release!"
94
- exit!
95
- end
96
- sh "git commit --allow-empty -a -m 'Release #{version}'"
97
- sh "git tag v#{version}"
98
- sh "git push origin master"
99
- sh "git push origin v#{version}"
100
- sh "gem push pkg/#{name}-#{version}.gem"
101
- end
102
-
103
- desc "Build #{gem_file} into the pkg directory"
104
- task :build => :gemspec do
105
- sh "mkdir -p pkg"
106
- sh "gem build #{gemspec_file}"
107
- sh "mv #{gem_file} pkg"
108
- end
109
-
110
- desc "Generate #{gemspec_file}"
111
- task :gemspec => :validate do
112
- # read spec file and split out manifest section
113
- spec = File.read(gemspec_file)
114
- head, manifest, tail = spec.split(" # = MANIFEST =\n")
115
-
116
- # replace name version and date
117
- replace_header(head, :name)
118
- replace_header(head, :version)
119
- replace_header(head, :date)
120
- #comment this out if your rubyforge_project has a different name
121
- replace_header(head, :rubyforge_project)
122
-
123
- # determine file list from git ls-files
124
- files = `git ls-files`.
125
- split("\n").
126
- sort.
127
- reject { |file| file =~ /^\./ }.
128
- reject { |file| file =~ /^(rdoc|pkg)/ }.
129
- map { |file| " #{file}" }.
130
- join("\n")
131
-
132
- # piece file back together and write
133
- manifest = " s.files = %w[\n#{files}\n ]\n"
134
- spec = [head, manifest, tail].join(" # = MANIFEST =\n")
135
- File.open(gemspec_file, 'w') { |io| io.write(spec) }
136
- puts "Updated #{gemspec_file}"
137
- end
138
-
139
- desc "Validate #{gemspec_file}"
140
- task :validate do
141
- libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
142
- unless libfiles.empty?
143
- puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
144
- exit!
145
- end
146
- unless Dir['VERSION*'].empty?
147
- puts "A `VERSION` file at root level violates Gem best practices."
148
- exit!
149
- end
150
- end
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+ gemspec :path => '../'
3
+
4
+ group :development, :test do
5
+ gem 'activesupport', '~> 3.2.0'
6
+ gem 'activerecord', '~> 3.2.0'
7
+ gem 'actionpack', '~> 3.2.0'
8
+ end
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+ gemspec :path => '../'
3
+
4
+ group :development, :test do
5
+ gem 'activesupport', '~> 4.0'
6
+ gem 'activerecord', '~> 4.0'
7
+ gem 'actionpack', '~> 4.0'
8
+ end
9
+
data/lib/timely.rb CHANGED
@@ -1,17 +1,21 @@
1
1
  module Timely
2
- VERSION = '0.0.2'
3
-
4
2
  require 'time'
5
3
  require 'date'
6
4
 
7
5
  require 'timely/string'
8
6
  require 'timely/date'
9
7
  require 'timely/time'
8
+ require 'timely/time_since'
10
9
  require 'timely/date_time'
11
10
  require 'timely/range'
12
11
  require 'timely/date_range'
13
12
  require 'timely/date_chooser'
14
13
  require 'timely/week_days'
15
- require 'timely/temporal_patterns'
14
+ require 'timely/temporal_patterns/finder'
15
+ require 'timely/temporal_patterns/frequency'
16
+ require 'timely/temporal_patterns/interval'
17
+ require 'timely/temporal_patterns/pattern'
16
18
  require 'timely/trackable_date_set'
19
+
20
+ require 'timely/railtie' if defined?(Rails::Railtie)
17
21
  end
data/lib/timely/date.rb CHANGED
@@ -10,6 +10,26 @@ module Timely
10
10
  end
11
11
 
12
12
  alias_method :at, :at_time
13
+
14
+ # returns true if date between from and to
15
+ # however if from and/or to are nil, it ignores that query
16
+ # e.g. date.between?(from, nil) is "date > from",
17
+ # date.between?(from, nil) is "date < to"
18
+ # date.between?(nil, nil) is true
19
+ def between?(from, to)
20
+ from = from.to_date if from && !from.is_a?(Date)
21
+ to = to.to_date if to && !to.is_a?(Date)
22
+
23
+ if from && to
24
+ self >= from && self <= to
25
+ elsif from
26
+ self >= from
27
+ elsif to
28
+ self <= to
29
+ else
30
+ true # i.e. no restrictive range return true
31
+ end
32
+ end
13
33
  end
14
34
  end
15
35
 
@@ -3,9 +3,9 @@ module Timely
3
3
  # Where is this used... so far only in one place, _date_range.html.haml
4
4
  # May be good to refactor this as well, after the class behaviour is refactored.
5
5
  INTERVALS = [
6
- {:code => 'w', :name => 'week(s)', :description =>
6
+ {:code => 'week', :name => 'week(s)', :description =>
7
7
  'Weekdays selected will be chosen every {{n}} weeks for the date range'},
8
- {:code => 'wom', :name => 'week of month', :description =>
8
+ {:code => 'week_of_month', :name => 'week of month', :description =>
9
9
  'Weekdays selected will be chosen in their {{ord}} occurance every month,
10
10
  e.g. if wednesday and thursday are selected, the first wednesday and
11
11
  first thursday are selected. Note: this may mean the booking is copied
@@ -20,6 +20,7 @@ module Timely
20
20
  @to = process_date(options[:to])
21
21
  @select = options[:select]
22
22
  @dates = options[:dates]
23
+ @specific_dates = options[:specific_dates]
23
24
  @interval = options[:interval]
24
25
  @weekdays = WeekDays.new(options[:weekdays]) if @select == 'weekdays'
25
26
  validate
@@ -40,7 +41,8 @@ module Timely
40
41
  # :to - The end of the date range
41
42
  #
42
43
  # You can either specify specific dates to be chosen each month:
43
- # :dates - A comma separated string of days of the month, e.g. 1,16
44
+ # :dates - A comma separated string of days of the month, e.g. 1,16
45
+ # :specific_dates - A comma separated string of dates, e.g. 26-10-2012, 03-11-2012, 01-01-2013
44
46
  #
45
47
  # or you can specify how to select the dates
46
48
  # :day - A hash of days, the index being the wday, e.g. 0 = sunday, and the value being 1 if chosen
@@ -60,6 +62,9 @@ module Timely
60
62
  when 'days'
61
63
  days = @dates.gsub(/\s/, '').split(',')
62
64
  all_days.select { |date| days.include?(date.mday.to_s) }
65
+ when 'specific_days'
66
+ days = @specific_dates.gsub(/\s/, '').split(',')
67
+ days.map(&:to_date)
63
68
  when 'weekdays'
64
69
  raise DateChooserException, "No days of the week selected" if @weekdays.weekdays.empty?
65
70
  raise DateChooserException, "No weekly interval selected" if @interval && @interval.empty?
@@ -67,12 +72,12 @@ module Timely
67
72
  all_days.select do |date|
68
73
  if @weekdays.has_day?(date.wday)
69
74
  case @interval[:unit]
70
- when 'w'
75
+ when 'week'
71
76
  # 0 = first week, 1 = second week, 2 = third week, etc.
72
77
  nth_week = (date - @from).to_i / 7
73
78
  # true every 2nd week (0, 2, 4, 6, etc.)
74
79
  (nth_week % @interval[:level].to_i).zero?
75
- when 'wom'
80
+ when 'week_of_month'
76
81
  week = @interval[:level].to_i
77
82
  (date.mday > (week-1)*7 && date.mday <= week*7)
78
83
  end
@@ -1,17 +1,30 @@
1
+ require 'date' # Ensure Date class is loaded for old rubies (1.8)
2
+
1
3
  module Timely
2
4
  class DateRange < ::Range
3
5
  def initialize(*args)
4
6
  if args.first.is_a?(Range)
5
- super(args.first.first, args.first.last)
7
+ date_range = args.first
8
+ DateRange.validate_range(date_range.first, date_range.last)
9
+ super(date_range.first.to_date, date_range.last.to_date)
6
10
  elsif args.size == 1 && args.first.is_a?(Date)
7
- super(args.first, args.first)
11
+ DateRange.validate_range(args.first, args.last)
12
+ super(args.first.to_date, args.first.to_date)
8
13
  else
9
- super(*args)
14
+ DateRange.validate_range(args.first, args.last)
15
+ super(args.first.to_date, args.last.to_date)
10
16
  end
11
17
  end
12
18
  alias_method :start_date, :first
13
19
  alias_method :end_date, :last
14
20
 
21
+ def self.validate_range(first, last)
22
+ raise ArgumentError, "Date range missing start date" if first.nil?
23
+ raise ArgumentError, "Date range missing end date" if last.nil?
24
+ raise ArgumentError, "Start date is not a date" unless first.is_a? Date
25
+ raise ArgumentError, "End date is not a date" unless last.is_a? Date
26
+ end
27
+
15
28
  def self.from_params(start_date, duration = nil)
16
29
  start_date = start_date.to_date
17
30
  duration = [1, duration.to_i].max
@@ -34,18 +47,42 @@ module Timely
34
47
  end
35
48
  alias_method :duration, :number_of_nights
36
49
 
37
- def to_s(fmt = '%b %Y')
38
- if first == last
39
- first.to_s(:short)
40
- elsif first == first.at_beginning_of_month && last == last.at_end_of_month
41
- if first.month == last.month
50
+ def to_s(fmt = '%b %Y', date_fmt = '%Y-%m-%d')
51
+ Timely::DateRange.to_s(first, last, fmt, date_fmt)
52
+ end
53
+
54
+ def self.to_s(first = nil, last = nil, month_fmt = '%b %Y', date_fmt = default_date_format, time_fmt = nil)
55
+ time_fmt ||= date_fmt + ' %H:%M'
56
+ is_date = first.is_a?(Date) || last.is_a?(Date)
57
+ fmt = is_date ? date_fmt : time_fmt
58
+ if first && last
59
+ if first == last
42
60
  first.strftime(fmt)
61
+ elsif first == first.at_beginning_of_month && last == last.at_end_of_month
62
+ if first.month == last.month
63
+ first.strftime(month_fmt)
64
+ else
65
+ "#{first.strftime(month_fmt)} to #{last.strftime(month_fmt)}"
66
+ end
43
67
  else
44
- "#{first.strftime(fmt)} to #{last.strftime(fmt)}"
68
+ "#{first.strftime(fmt)} to #{last.strftime(fmt)}#{" (inclusive)" if is_date}"
45
69
  end
70
+ elsif first
71
+ "on or after #{first.strftime(fmt)}"
72
+ elsif last
73
+ "on or before #{last.strftime(fmt)}"
46
74
  else
47
- "#{first.to_s(:short)} to #{last.to_s(:short)}"
75
+ "no date range"
48
76
  end
49
77
  end
78
+
79
+
80
+ private
81
+
82
+ def self.default_date_format
83
+ # ::Date as we want Ruby's Date not Timely::Date
84
+ date_format = ::Date::DATE_FORMATS[:short] if ::Date.const_defined?('DATE_FORMATS')
85
+ date_format || '%Y-%m-%d'
86
+ end
50
87
  end
51
88
  end
data/lib/timely/rails.rb CHANGED
@@ -1,4 +1,11 @@
1
1
  require 'timely/rails/extensions'
2
- ActiveRecord::Base.extend Timely::Extensions
3
- require 'timely/rails/season'
4
- require 'timely/rails/date_group'
2
+ if defined?(ActiveRecord)
3
+ ActiveRecord::Base.extend Timely::Extensions
4
+ require 'timely/rails/season'
5
+ require 'timely/rails/date_group'
6
+ end
7
+ require 'timely/rails/date_range_validity_module'
8
+ require 'timely/rails/calendar_tag'
9
+ require 'timely/rails/date_time'
10
+ require 'timely/rails/date'
11
+ require 'timely/rails/period'