Project

week_sauce

0.01
No commit activity in last 3 years
No release in over 3 years
A simple gem to serialize selected days of the week as a bitmask
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0
~> 0.3.29
 Project Readme

WeekSauce Build Status Gem Version Code Climate

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.

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.

Installation

It's a gem, so:

gem install week_sauce

or, if you're using Bundler, put this in your Gemfile:

gem 'week_sauce'

The Basics

week = WeekSauce.new(16) # init with bitmask (optional)
week.blank?     #=> false
week.one?       #=> true
week.thursday?  #=> true

week = WeekSauce.new     # defaults to a zero-bitmask
week.blank? #=> true

# Mark the weekend
week.set(:saturday, :sunday)

from = Time.parse("2013-04-01") # A Monday
week.next_date(from)            #=> Sat, 06 Apr 2013

week.dates_in(from..from + 1.week) => [Sat, 06 Apr 2013 , Sun, 07 Apr 2013]

Usage with ActiveRecord

class Workout < ActiveRecord::Base
  serialize :days, WeekSauce
end

workout = Workout.find_by_kind("Weights")
workout.days.inspect #=> "20: Tuesday, Thursday"

workout.days.set!(:tuesday, :thursday) # sets only those days
workout.save

The underlying days database column can be either a string or integer type.

API

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:

time = Time.parse("2013-04-01") # A Monday

# These are all equivalent
week.monday        = true
week[:monday]      = true
week["monday"]     = true # case-insensitive
week[1]            = true
week["1"]          = true
week[time]         = true
week[time.to_date] = true

# And these are equivalent too
week.monday        #=> true
week.monday?       #=> true
week[:monday]      #=> true
week["monday"]     #=> true (again, case-insensitive)
week[1]            #=> true
week["1"]          #=> true
week[time]         #=> true
week[time.to_date] #=> true

Note that similar to Date#wday, Sunday is 0, Monday is 1 and so forth.

Several days can be set or unset using the so-named methods:

# arguments can also be Fixnums or Date/Time objects
week.set(:monday, :wednesday)    # set those days to true
week.unset(:monday, :wednesday)  # set those days to false

These also have "exclusive" versions:

week.set!(:monday, :wednesday)   # sets *only* those days to true, all others to false
week.unset!(:monday, :wednesday) # sets *only* those days to false, all others to true

There are also a few value-checking methods:

week.blank?  # true if no days are set
week.one?    # true if exactly 1 day is set
week.any?    # true if at least 1 day is set
week.many?   # true if at least 2 days are set
week.all?    # true if all days are set

# The == comparison operator works with other WeekSauce objects and Fixnums
week == other_week #=> true if the bitmask values match
week == 123        #=> true if the bitmask value == 123

And a couple of methods to find dates matching the week's bitmask:

week.dates_in(from..to)   # find matching dates in a range of dates

week.next_date            # finds next matching date from today
week.next_date(some_date) # finds next matching date from (and including) some_date

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.

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.

Lastly, a few utility methods:

week.to_i    #=> the raw integer bitmask
week.to_a    #=> array of the selected days' names, e.g. [:monday, :thursday]
week.to_hash #=> hash with day names as keys, and the day's boolean state as value
week.count   #=> number of days set (0..7)
week.inspect #=> string describing the bitmask values and days, e.g. "3: Sunday, Monday"

Odds and ends

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!).

The code has good test coverage (using RSpec), and is tested against a few 1.9 and 2.0 rubies using TravisCI.