timely 0.0.2 → 0.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 (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'