week_sauce 0.0.1 → 0.0.2

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 (3) hide show
  1. data/README.md +123 -74
  2. data/lib/week_sauce.rb +66 -47
  3. metadata +19 -3
data/README.md CHANGED
@@ -1,89 +1,138 @@
1
- # WeekSauce
2
-
3
- WeekSauce is a simple class that functions as a days-of-the-week bitmask.
4
- Useful for things that repeat weekly, and/or can occur or one or more days
5
- of the week.
6
-
7
- Extracted from a Rails app, it's intended to be used an ActiveRecord
8
- attribute serializer, but it should work fine outside of Rails.
9
-
10
- ## Basic usage
11
-
12
- week = WeekSauce.new(16) # set a specific bitmask
13
- week.blank? #=> false
14
- week.one? #=> true
15
- week.thursday? #=> true
16
-
17
- week = WeekSauce.new # defaults to a zero-bitmask
18
- week.blank? #=> true
19
-
20
- # Mark off the weekend
21
- week.set(:saturday, :sunday)
22
-
23
- from = Time.parse("2013-04-01") # A Monday
24
- week.next_date(from) #=> "2013-04-06" (a Saturday)
25
-
26
- week.dates_in(from..from + 1.week) => ["2013-04-06", "2013-04-07"]
27
-
28
- ## Rails usage
29
-
30
- class Workout < ActiveRecord::Base
31
- serialize :days, WeekSauce
32
- end
33
-
34
- workout = Workout.find_by_kind("Weights")
35
- workout.days.to_s #=> "Monday, Wednesday"
36
- workout.days.set!(:tuesday, :thursday) # sets only those days
37
- workout.save
1
+ # WeekSauce [![Build Status](https://travis-ci.org/Flambino/week_sauce.png?branch=master)](https://travis-ci.org/Flambino/week_sauce) [![Gem Version](https://badge.fury.io/rb/week_sauce.png)](http://badge.fury.io/rb/week_sauce) [![Code Climate](https://codeclimate.com/github/Flambino/week_sauce.png)](https://codeclimate.com/github/Flambino/week_sauce)
2
+
3
+ WeekSauce is a simple class that functions as a days-of-the-week bitmask. Useful for things that repeat weekly, and/or can occur on one or more days of the week.
4
+
5
+ It was extracted from a Rails app, and is primarily intended to used as an ActiveRecord attribute serializer, but it should work fine outside of Rails too.
6
+
7
+ ## Installation
8
+
9
+ It's a gem, so:
10
+
11
+ gem install week_sauce
12
+
13
+ or, if you're using Bundler, put this in your Gemfile:
14
+
15
+ gem 'week_sauce'
16
+
17
+ ## The Basics
18
+
19
+ ``` ruby
20
+ week = WeekSauce.new(16) # init with bitmask (optional)
21
+ week.blank? #=> false
22
+ week.one? #=> true
23
+ week.thursday? #=> true
24
+
25
+ week = WeekSauce.new # defaults to a zero-bitmask
26
+ week.blank? #=> true
27
+
28
+ # Mark the weekend
29
+ week.set(:saturday, :sunday)
30
+
31
+ from = Time.parse("2013-04-01") # A Monday
32
+ week.next_date(from) #=> Sat, 06 Apr 2013
33
+
34
+ week.dates_in(from..from + 1.week) => [Sat, 06 Apr 2013 , Sun, 07 Apr 2013]
35
+ ```
36
+
37
+ ## Usage with ActiveRecord
38
+
39
+ ``` ruby
40
+ class Workout < ActiveRecord::Base
41
+ serialize :days, WeekSauce
42
+ end
43
+
44
+ workout = Workout.find_by_kind("Weights")
45
+ workout.days.inspect #=> "20: Tuesday, Thursday"
46
+
47
+ workout.days.set!(:tuesday, :thursday) # sets only those days
48
+ workout.save
49
+ ```
50
+
51
+ The underlying `days` database column can be either a string or integer type.
38
52
 
39
53
  ## API
40
54
 
41
- Individual days can be set and read using Fixnums, symbols or Date/Time
42
- objects. Additionally, there are named methods for getting and setting
43
- each of the week's days:
44
-
45
- # These are all equivalent
46
- week.monday = true
47
- week[:monday] = true
48
- week[1] = true
49
-
50
- time = Time.parse("2013-04-01") # A Monday
51
- week[time] = true
52
- week[time.to_date] = true
53
-
54
- # And these are equivalent too
55
- week.monday #=> true
56
- week.monday? #=> true
57
- week[:monday] #=> true
58
- week[1] #=> true
59
- week[time] #=> true
55
+ Individual days can be set and read using Fixnums, strings, symbols or Date/Time objects. Additionally, there are named methods for getting and setting each of the week's days:
56
+
57
+ ``` ruby
58
+ time = Time.parse("2013-04-01") # A Monday
59
+
60
+ # These are all equivalent
61
+ week.monday = true
62
+ week[:monday] = true
63
+ week["monday"] = true # case-insensitive
64
+ week[1] = true
65
+ week["1"] = true
66
+ week[time] = true
67
+ week[time.to_date] = true
68
+
69
+ # And these are equivalent too
70
+ week.monday #=> true
71
+ week.monday? #=> true
72
+ week[:monday] #=> true
73
+ week["monday"] #=> true (again, case-insensitive)
74
+ week[1] #=> true
75
+ week["1"] #=> true
76
+ week[time] #=> true
77
+ week[time.to_date] #=> true
78
+ ```
60
79
 
61
80
  _Note that similar to `Date#wday`, Sunday is `0`, Monday is `1` and so forth._
62
81
 
63
- There are also a few value-checking methods:
82
+ Several days can be set or unset using the so-named methods:
64
83
 
65
- week.blank? # true if no days are set
66
- week.one? # true if exactly 1 day is set
67
- week.any? # true if at least 1 day is set
68
- week.many? # true if at least 2 days are set
69
- week.all? # true if all days are set
84
+ ``` ruby
85
+ # arguments can also be Fixnums or Date/Time objects
86
+ week.set(:monday, :wednesday) # set those days to true
87
+ week.unset(:monday, :wednesday) # set those days to false
88
+ ```
70
89
 
71
- Several days can be set or unset using the so-named methods:
90
+ These also have "exclusive" versions:
72
91
 
73
- # arguments can also be Fixnums or Date/Time objects
74
- week.set(:monday, :wednesday) # set those days to true
75
- week.unset(:monday, :wednesday) # set those days to false
92
+ ``` ruby
93
+ week.set!(:monday, :wednesday) # sets *only* those days to true, all others to false
94
+ week.unset!(:monday, :wednesday) # sets *only* those days to false, all others to true
95
+ ```
96
+
97
+ There are also a few value-checking methods:
76
98
 
77
- These also have "exclusive" versions
99
+ ``` ruby
100
+ week.blank? # true if no days are set
101
+ week.one? # true if exactly 1 day is set
102
+ week.any? # true if at least 1 day is set
103
+ week.many? # true if at least 2 days are set
104
+ week.all? # true if all days are set
78
105
 
79
- week.set!(:monday, :wednesday) # sets *only* those days to true, all others to false
80
- week.unset!(:monday, :wednesday) # set *only* those days to false, all others to true
106
+ # The == comparison operator works with other WeekSauce objects and Fixnums
107
+ week == other_week #=> true if the bitmask values match
108
+ week == 123 #=> true if the bitmask value == 123
109
+ ```
81
110
 
82
- Lastly, dates matching the week's bitmask can be calculated
111
+ And a couple of methods to find dates matching the week's bitmask:
83
112
 
84
- week.dates_in(from..to) # find matching dates in a range
85
-
86
- week.next_date # finds next matching date from today
87
- week.next_date(some_date) # finds next matching date from (and including) some_date
113
+ ``` ruby
114
+ week.dates_in(from..to) # find matching dates in a range of dates
115
+
116
+ week.next_date # finds next matching date from today
117
+ week.next_date(some_date) # finds next matching date from (and including) some_date
118
+ ```
119
+
120
+ In this example, `some_date` may be either a `Date` or a `Time` object. If it's the latter, it'll be converted using `#to_date`.
88
121
 
89
122
  If ActiveSupport's time zone support is available, `next_date` with no argument will default to the time zone-aware `Date.current` instead of `Date.today`.
123
+
124
+ Lastly, a few utility methods:
125
+
126
+ ``` ruby
127
+ week.to_i #=> the raw integer bitmask
128
+ week.to_a #=> array of the selected days' names, e.g. [:monday, :thursday]
129
+ week.to_hash #=> hash with day names as keys, and the day's boolean state as value
130
+ week.count #=> number of days set (0..7)
131
+ week.inspect #=> string describing the bitmask values and days, e.g. "3: Sunday, Monday"
132
+ ```
133
+
134
+ ## Odds and ends
135
+
136
+ The gem was extracted from a Rails 3.2 app, and requires Ruby 1.9.2 or higher. The requirement is undoubtedly overkill, but the specs currently use the 1.9+ hash syntax. It could be converted very easily though (feel free!).
137
+
138
+ The code has good test coverage (using RSpec), and is tested against [a few 1.9 and 2.0 rubies using TravisCI](https://travis-ci.org/Flambino/week_sauce).
data/lib/week_sauce.rb CHANGED
@@ -1,40 +1,44 @@
1
1
  require 'date'
2
-
3
- # WeekSauce is a simple class that functions as a days-of-the-week bitmask.
4
- # Useful for things that repeat weekly, and/or can occur or one or more days
5
- # of the week.
6
2
  #
7
- # Extracted from a Rails app, it's intended to be used an ActiveRecord
8
- # attribute serializer, but it should work fine outside of Rails.
3
+ # {<img src="https://travis-ci.org/Flambino/week_sauce.png?branch=master" alt="Build Status" />}[https://travis-ci.org/Flambino/week_sauce]
4
+ # {<img src="https://badge.fury.io/rb/week_sauce.png" alt="Gem Version" />}[http://badge.fury.io/rb/week_sauce]
5
+ # {<img src="https://codeclimate.com/github/Flambino/week_sauce.png" />}[https://codeclimate.com/github/Flambino/week_sauce]
9
6
  #
10
- # == Basic usage
7
+ # WeekSauce is a simple class that functions as a days-of-the-week bitmask. Useful for things that repeat weekly, and/or can occur on one or more days of the week.
11
8
  #
12
- # week = WeekSauce.new(16) # set a specific bitmask
13
- # week.blank? #=> false
14
- # week.one? #=> true
15
- # week.thursday? #=> true
16
- #
17
- # week = WeekSauce.new # defaults to a zero-bitmask
18
- # week.blank? #=> true
19
- #
20
- # # Mark off the weekend
21
- # week.set(:saturday, :sunday)
22
- #
23
- # from = Time.parse("2013-04-01") # A Monday
24
- # week.next_date(from) #=> "2013-04-06" (a Saturday)
25
- #
26
- # week.dates_in(from..from + 1.week) => ["2013-04-06", "2013-04-07"]
9
+ # It was extracted from a Rails app, and is primarily intended to used as an ActiveRecord attribute serializer, but it should work fine outside of Rails too.
27
10
  #
28
- # == Rails usage
11
+ # == The Basics
29
12
  #
30
- # class Workout < ActiveRecord::Base
31
- # serialize :days, WeekSauce
32
- # end
33
- #
34
- # workout = Workout.find_by_kind("Weights")
35
- # workout.days.to_s #=> "Monday, Wednesday"
36
- # workout.days.set!(:tuesday, :thursday) # sets only those days
37
- # workout.save
13
+ # week = WeekSauce.new(16) # init with bitmask (optional)
14
+ # week.blank? #=> false
15
+ # week.one? #=> true
16
+ # week.thursday? #=> true
17
+ #
18
+ # week = WeekSauce.new # defaults to a zero-bitmask
19
+ # week.blank? #=> true
20
+ #
21
+ # # Mark the weekend
22
+ # week.set(:saturday, :sunday)
23
+ #
24
+ # from = Time.parse("2013-04-01") # A Monday
25
+ # week.next_date(from) #=> Sat, 06 Apr 2013
26
+ #
27
+ # week.dates_in(from..from + 1.week) => [Sat, 06 Apr 2013 , Sun, 07 Apr 2013]
28
+ #
29
+ # == Usage with ActiveRecord
30
+ #
31
+ # class Workout < ActiveRecord::Base
32
+ # serialize :days, WeekSauce
33
+ # end
34
+ #
35
+ # workout = Workout.find_by_kind("Weights")
36
+ # workout.days.inspect #=> "20: Tuesday, Thursday"
37
+ #
38
+ # workout.days.set!(:tuesday, :thursday) # sets only those days
39
+ # workout.save
40
+ #
41
+ # The underlying `days` database column can be either a string or integer type.
38
42
  #
39
43
  class WeekSauce
40
44
  MAX_VALUE = 2**7 - 1
@@ -54,7 +58,7 @@ class WeekSauce
54
58
 
55
59
  # ActiveRecord attribute serialization support
56
60
  #
57
- # Dump a WeekSauce instance to a stringified instance bitmask
61
+ # Dump a WeekSauce instance to a stringified bitmask value
58
62
  def dump(instance)
59
63
  if instance.is_a?(self)
60
64
  instance.to_i.to_s
@@ -73,7 +77,7 @@ class WeekSauce
73
77
  @value = [[0, value.to_i].max, MAX_VALUE].min
74
78
  end
75
79
 
76
- # Compare this instance against another instance, or a Fixnum
80
+ # Compare this instance against another instance or a Fixnum
77
81
  def ==(arg)
78
82
  case arg
79
83
  when self.class, Fixnum
@@ -110,6 +114,7 @@ class WeekSauce
110
114
  any? && !one?
111
115
  end
112
116
 
117
+ # Create the day=, day, and day? methods
113
118
  DAY_BITS.each do |day, bit|
114
119
  define_method(day) do
115
120
  get_bit bit
@@ -121,12 +126,15 @@ class WeekSauce
121
126
  end
122
127
  end
123
128
 
124
- # Returns +true+ if the given weekday is set, +false+ if it isn't,
129
+ # Returns +true+ if the given day is set, +false+ if it isn't,
125
130
  # and +nil+ if the argument was invalid.
126
131
  #
127
- # The +wday+ argument can be a Fixnum from 0 (Sunday) to 6 (Saturday),
128
- # a symbol specifying the day's name (e.g. +:tuesday+, +:friday+), or
129
- # a Date or Time instance
132
+ # The +wday+ argument can be
133
+ # - a Fixnum from 0 (Sunday) to 6 (Saturday),
134
+ # - a day-name symbol, e.g. +:tuesday+, +:friday+,
135
+ # - a day-name string (case-insensitive), e.g. <tt>"Monday"</tt>, <tt>"sunday"</tt>
136
+ # - a Time object, or
137
+ # - a Date object
130
138
  def [](wday)
131
139
  get_bit coerce_to_bit(wday)
132
140
  end
@@ -145,7 +153,7 @@ class WeekSauce
145
153
  end
146
154
  end
147
155
 
148
- # Exclusive version of #set - clears the week, and sets only
156
+ # Exclusive version of #set. Clears the week, and sets only
149
157
  # the days passed. Returns +self+
150
158
  def set!(*days)
151
159
  blank!
@@ -161,7 +169,7 @@ class WeekSauce
161
169
  end
162
170
  end
163
171
 
164
- # Exclusive version of #unset - sets all days to true and
172
+ # Exclusive version of #unset. Sets all days to true and
165
173
  # then unsets days passed. Returns +self+
166
174
  def unset!(*days)
167
175
  all!
@@ -191,6 +199,17 @@ class WeekSauce
191
199
  DAY_NAMES.select { |day| self[day] }
192
200
  end
193
201
 
202
+ # Returns a hash where the keys are the week's 7 days
203
+ # as symbols, and the values are booleans
204
+ def to_hash
205
+ Hash[ DAY_NAMES.map { |day| [day, self[day]] } ]
206
+ end
207
+
208
+ # Returns the number of "set" days
209
+ def count
210
+ to_a.count
211
+ end
212
+
194
213
  # Returns a string with the bitmask value and a list of
195
214
  # "set" days, or a simple message if all/no days are set
196
215
  def inspect
@@ -204,18 +223,15 @@ class WeekSauce
204
223
  end
205
224
  end
206
225
 
207
- # Returns a hash where the keys are the week's 7 days
208
- # as symbols, and the values are booleans
209
- def to_hash
210
- Hash[ DAY_NAMES.map { |day| [day, self[day]] } ]
211
- end
212
-
213
226
  # Return the next date matching the bitmask, or +nil+ if the
214
227
  # week is blank.
215
228
  #
216
229
  # If no +from_date+ argument is given, it'll default to
217
230
  # <tt>Date.current</tt> if ActiveSupport is available,
218
- # or <tt>Date.today</tt> otherwise.
231
+ # otherwise it'll use <tt>Date.today</tt>.
232
+ #
233
+ # If +from_date+ argument can be a +Date+ or a +Time+ object
234
+ # (the latter will be converted using +#to_date+)
219
235
  #
220
236
  # If +from_date+ is given, #next_date will return the first
221
237
  # matching date from - and including - +from_date+
@@ -267,10 +283,13 @@ class WeekSauce
267
283
  case wday
268
284
  when Symbol
269
285
  DAY_BITS[wday]
270
- when Fixnum
286
+ when Fixnum, /\A[0-6]\Z/
287
+ wday = wday.to_i
271
288
  (0..6).include?(wday) ? 2**wday : nil
272
289
  when Date, Time
273
290
  2**wday.wday
291
+ when /\A(sun|mon|tues|wednes|thurs|fri|satur)day\Z/i
292
+ DAY_BITS[wday.downcase.to_sym]
274
293
  end
275
294
  end
276
295
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: week_sauce
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-03 00:00:00.000000000 Z
12
+ date: 2013-06-04 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
14
30
  - !ruby/object:Gem::Dependency
15
31
  name: tzinfo
16
32
  requirement: !ruby/object:Gem::Requirement
@@ -64,7 +80,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
64
80
  requirements:
65
81
  - - ! '>='
66
82
  - !ruby/object:Gem::Version
67
- version: 1.9.3
83
+ version: 1.9.2
68
84
  required_rubygems_version: !ruby/object:Gem::Requirement
69
85
  none: false
70
86
  requirements: