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.
- data/README.md +123 -74
- data/lib/week_sauce.rb +66 -47
- 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
|
-
|
5
|
-
of
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
1
|
+
# WeekSauce [](https://travis-ci.org/Flambino/week_sauce) [](http://badge.fury.io/rb/week_sauce) [](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
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
82
|
+
Several days can be set or unset using the so-named methods:
|
64
83
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
90
|
+
These also have "exclusive" versions:
|
72
91
|
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
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
|
-
|
80
|
-
|
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
|
-
|
111
|
+
And a couple of methods to find dates matching the week's bitmask:
|
83
112
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
#
|
8
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
# ==
|
11
|
+
# == The Basics
|
29
12
|
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
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
|
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
|
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
|
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
|
128
|
-
# a
|
129
|
-
# a
|
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
|
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
|
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
|
-
#
|
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.
|
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-
|
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.
|
83
|
+
version: 1.9.2
|
68
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
85
|
none: false
|
70
86
|
requirements:
|