Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

week_start option added to rails app config #5339

Merged
merged 1 commit into from
Sep 18, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions activesupport/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##

* `Date.beginning_of_week` thread local and `beginning_of_week` application config option added (default is Monday). *Innokenty Mikhailov*

* An optional block can be passed to `config_accessor` to set its default value

class User
Expand Down
23 changes: 23 additions & 0 deletions activesupport/lib/active_support/core_ext/date/calculations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,29 @@ class Date
include DateAndTime::Calculations

class << self
attr_accessor :beginning_of_week_default

# Returns the week start (e.g. :monday) for the current request, if this has been set (via Date.beginning_of_week=).
# If <tt>Date.beginning_of_week</tt> has not been set for the current request, returns the week start specified in <tt>config.beginning_of_week</tt>.
# If no config.beginning_of_week was specified, returns :monday.
def beginning_of_week
Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
end

# Sets <tt>Date.beginning_of_week</tt> to a week start (e.g. :monday) for current request/thread.
#
# This method accepts any of the following day symbols:
# :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
def beginning_of_week=(week_start)
Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
end

# Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol.
def find_beginning_of_week!(week_start)
raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.keys.include?(week_start)
week_start
end

# Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
def yesterday
::Date.current.yesterday
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,11 @@ def beginning_of_year
alias :at_beginning_of_year :beginning_of_year

# Returns a new date/time representing the given day in the next week.
# Default is :monday.
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
# DateTime objects have their time set to 0:00.
def next_week(day = :monday)
first_hour{ weeks_since(1).beginning_of_week.days_since(DAYS_INTO_WEEK[day]) }
def next_week(start_day = Date.beginning_of_week)
first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(start_day)) }
end

# Short-hand for months_since(1).
Expand All @@ -131,10 +132,11 @@ def next_year
end

# Returns a new date/time representing the given day in the previous week.
# Default is :monday.
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
# DateTime objects have their time set to 0:00.
def prev_week(day = :monday)
first_hour{ weeks_ago(1).beginning_of_week.days_since(DAYS_INTO_WEEK[day]) }
def prev_week(start_day = Date.beginning_of_week)
first_hour{ weeks_ago(1).beginning_of_week.days_since(days_span(start_day)) }
end
alias_method :last_week, :prev_week

Expand All @@ -157,31 +159,44 @@ def prev_year
alias_method :last_year, :prev_year

# Returns the number of days to the start of the week on the given day.
# Default is :monday.
def days_to_week_start(start_day = :monday)
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
def days_to_week_start(start_day = Date.beginning_of_week)
start_day_number = DAYS_INTO_WEEK[start_day]
current_day_number = wday != 0 ? wday - 1 : 6
(current_day_number - start_day_number) % 7
end

# Returns a new date/time representing the start of this week on the given day.
# Default is :monday.
# DateTime objects have their time set to 0:00.
def beginning_of_week(start_day = :monday)
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
# +DateTime+ objects have their time set to 0:00.
def beginning_of_week(start_day = Date.beginning_of_week)
result = days_ago(days_to_week_start(start_day))
acts_like?(:time) ? result.midnight : result
end
alias :at_beginning_of_week :beginning_of_week
alias :monday :beginning_of_week

# Returns Monday of this week assuming that week starts on Monday.
# +DateTime+ objects have their time set to 0:00.
def monday
beginning_of_week(:monday)
end

# Returns a new date/time representing the end of this week on the given day.
# Default is :monday (i.e end of Sunday).
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
# DateTime objects have their time set to 23:59:59.
def end_of_week(start_day = :monday)
def end_of_week(start_day = Date.beginning_of_week)
last_hour{ days_since(6 - days_to_week_start(start_day)) }
end
alias :at_end_of_week :end_of_week
alias :sunday :end_of_week

# Returns Sunday of this week assuming that week starts on Monday.
# +DateTime+ objects have their time set to 23:59:59.
def sunday
end_of_week(:monday)
end

# Returns a new date/time representing the end of the month.
# DateTime objects will have a time set to 23:59:59.
Expand Down Expand Up @@ -209,5 +224,9 @@ def last_hour
result = yield
acts_like?(:time) ? result.end_of_day : result
end

def days_span(day)
(DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,9 @@ def all_day
beginning_of_day..end_of_day
end

# Returns a Range representing the whole week of the current time. Week starts on start_day (default is :monday, i.e. end of Sunday).
def all_week(start_day = :monday)
# Returns a Range representing the whole week of the current time.
# Week starts on start_day, default is <tt>Date.week_start</tt> or <tt>config.week_start</tt> when set.
def all_week(start_day = Date.beginning_of_week)
beginning_of_week(start_day)..end_of_week(start_day)
end

Expand Down
9 changes: 9 additions & 0 deletions activesupport/lib/active_support/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ class Railtie < Rails::Railtie
Time.zone_default = zone_default
end

# Sets the default week start
# If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised.
initializer "active_support.initialize_beginning_of_week" do |app|
require 'active_support/core_ext/date/calculations'
beginning_of_week_default = Date.find_beginning_of_week!(app.config.beginning_of_week)

Date.beginning_of_week_default = beginning_of_week_default
end

initializer "active_support.set_configs" do |app|
app.config.active_support.each do |k, v|
k = "#{k}="
Expand Down
50 changes: 50 additions & 0 deletions activesupport/test/core_ext/date_and_time_behavior.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ def test_next_week
assert_equal date_time_init(2006,11,1,0,0,0), date_time_init(2006,10,23,0,0,0).next_week(:wednesday)
end

def test_next_week_with_default_beginning_of_week_set
with_bw_default(:tuesday) do
assert_equal Time.local(2012, 3, 28), Time.local(2012, 3, 21).next_week(:wednesday)
assert_equal Time.local(2012, 3, 31), Time.local(2012, 3, 21).next_week(:saturday)
assert_equal Time.local(2012, 3, 27), Time.local(2012, 3, 21).next_week(:tuesday)
assert_equal Time.local(2012, 4, 02), Time.local(2012, 3, 21).next_week(:monday)
end
end

def test_next_month_on_31st
assert_equal date_time_init(2005,9,30,15,15,10), date_time_init(2005,8,31,15,15,10).next_month
end
Expand All @@ -121,6 +130,15 @@ def test_prev_week
assert_equal date_time_init(2006,11,15,0,0,0), date_time_init(2006,11,23,0,0,0).prev_week(:wednesday)
end

def test_prev_week_with_default_beginning_of_week
with_bw_default(:tuesday) do
assert_equal Time.local(2012, 3, 14), Time.local(2012, 3, 21).prev_week(:wednesday)
assert_equal Time.local(2012, 3, 17), Time.local(2012, 3, 21).prev_week(:saturday)
assert_equal Time.local(2012, 3, 13), Time.local(2012, 3, 21).prev_week(:tuesday)
assert_equal Time.local(2012, 3, 19), Time.local(2012, 3, 21).prev_week(:monday)
end
end

def test_prev_month_on_31st
assert_equal date_time_init(2004,2,29,10,10,10), date_time_init(2004,3,31,10,10,10).prev_month
end
Expand Down Expand Up @@ -151,6 +169,18 @@ def test_days_to_week_start
assert_equal 3, date_time_init(2011,11,9,0,0,0).days_to_week_start(:sunday)
end

def test_days_to_week_start_with_default_set
with_bw_default(:friday) do
assert_equal 6, Time.local(2012,03,8,0,0,0).days_to_week_start
assert_equal 5, Time.local(2012,03,7,0,0,0).days_to_week_start
assert_equal 4, Time.local(2012,03,6,0,0,0).days_to_week_start
assert_equal 3, Time.local(2012,03,5,0,0,0).days_to_week_start
assert_equal 2, Time.local(2012,03,4,0,0,0).days_to_week_start
assert_equal 1, Time.local(2012,03,3,0,0,0).days_to_week_start
assert_equal 0, Time.local(2012,03,2,0,0,0).days_to_week_start
end
end

def test_beginning_of_week
assert_equal date_time_init(2005,1,31,0,0,0), date_time_init(2005,2,4,10,10,10).beginning_of_week
assert_equal date_time_init(2005,11,28,0,0,0), date_time_init(2005,11,28,0,0,0).beginning_of_week #monday
Expand Down Expand Up @@ -183,4 +213,24 @@ def test_end_of_year
assert_equal date_time_init(2007,12,31,23,59,59,Rational(999999999, 1000)), date_time_init(2007,2,22,10,10,10).end_of_year
assert_equal date_time_init(2007,12,31,23,59,59,Rational(999999999, 1000)), date_time_init(2007,12,31,10,10,10).end_of_year
end

def test_monday_with_default_beginning_of_week_set
with_bw_default(:saturday) do
assert_equal date_time_init(2012,9,17,0,0,0), date_time_init(2012,9,18,0,0,0).monday
end
end

def test_sunday_with_default_beginning_of_week_set
with_bw_default(:wednesday) do
assert_equal date_time_init(2012,9,23,23,59,59, Rational(999999999, 1000)), date_time_init(2012,9,19,0,0,0).sunday
end
end

def with_bw_default(bw = :monday)
old_bw = Date.beginning_of_week
Date.beginning_of_week = bw
yield
ensure
Date.beginning_of_week = old_bw
end
end
4 changes: 4 additions & 0 deletions guides/source/configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ NOTE. The `config.asset_path` configuration is ignored if the asset pipeline is

* `config.time_zone` sets the default time zone for the application and enables time zone awareness for Active Record.

* `config.beginning_of_week` sets the default beginning of week for the application. Accepts a valid week day symbol (e.g. `:monday`).

* `config.whiny_nils` enables or disables warnings when a certain set of methods are invoked on `nil` and it does not respond to them. Defaults to true in development and test environments.

### Configuring Assets
Expand Down Expand Up @@ -696,6 +698,8 @@ Below is a comprehensive list of all the initializers found in Rails in the orde

* `active_support.initialize_time_zone` Sets the default time zone for the application based on the `config.time_zone` setting, which defaults to "UTC".

* `active_support.initialize_beginning_of_week` Sets the default beginnig of week for the application based on `config.beginning_of_week` setting, which defaults to `:monday`.

* `action_dispatch.configure` Configures the `ActionDispatch::Http::URL.tld_length` to be set to the value of `config.action_dispatch.tld_length`.

* `action_view.cache_asset_ids` Sets `ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids` to `false` when Active Support loads, but only if `config.cache_classes` is too.
Expand Down
3 changes: 2 additions & 1 deletion railties/lib/rails/application/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Configuration < ::Rails::Engine::Configuration
:railties_order, :relative_url_root, :secret_token,
:serve_static_assets, :ssl_options, :static_cache_control, :session_options,
:time_zone, :reload_classes_only_on_change,
:queue, :queue_consumer
:queue, :queue_consumer, :beginning_of_week

attr_writer :log_level
attr_reader :encoding
Expand All @@ -31,6 +31,7 @@ def initialize(*)
@session_store = :cookie_store
@session_options = {}
@time_zone = "UTC"
@beginning_of_week = :monday
@log_level = nil
@middleware = app_middleware
@generators = app_generators
Expand Down
22 changes: 22 additions & 0 deletions railties/test/application/configuration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,28 @@ def index
end
end

test "valid beginning of week is setup correctly" do
add_to_config <<-RUBY
config.root = "#{app_path}"
config.beginning_of_week = :wednesday
RUBY

require "#{app_path}/config/environment"

assert_equal :wednesday, Rails.application.config.beginning_of_week
end

test "raises when an invalid beginning of week is defined in the config" do
add_to_config <<-RUBY
config.root = "#{app_path}"
config.beginning_of_week = :invalid
RUBY

assert_raise(ArgumentError) do
require "#{app_path}/config/environment"
end
end

test "config.action_controller.perform_caching = false" do
make_basic_app do |app|
app.config.action_controller.perform_caching = false
Expand Down