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.
- 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 [![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
|
-
|
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:
|