week_sauce 0.0.1 → 0.0.2

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