named_scope_for_time_attr 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ == 1.0.0 2009-03-02
2
+
3
+ * Initial release
4
+
@@ -0,0 +1,12 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ init.rb
7
+ lib/named_scope_for_time_attr.rb
8
+ lib/named_scope_for_time_attr/named_scope_for_time_attr.rb
9
+ script/destroy
10
+ script/generate
11
+ test/test_helper.rb
12
+ test/test_named_scope_for_time_attr.rb
@@ -0,0 +1,4 @@
1
+
2
+ For more information on named_scope_for_time_attr,
3
+ see http://nsfta.rubyforge.org/
4
+
@@ -0,0 +1,73 @@
1
+ = named_scope_for_time_attr
2
+
3
+ All information can be found here: http://nsfta.rubyforge.org/
4
+
5
+ == DESCRIPTION:
6
+
7
+ Provides the ActiveRecord helper method <tt>named_scope_for_time_attr</tt>.
8
+ Given the name of a time attribute of an ActiveRecord model
9
+ (e.g. <tt>created_at</tt> of <tt>Claim</tt>) it defines a correspondent
10
+ named scope for easy access to an arbitrarily time range (scope).
11
+
12
+ == FEATURES/PROBLEMS:
13
+
14
+ * Defines a named scope for the given time attribute
15
+ * Allows easy access to an arbitrarily time range
16
+ * Defines a named scope <tt>order</tt> for easy ordering of the result
17
+
18
+ == SYNOPSIS:
19
+
20
+ Define the named scope <tt>created_at</tt> and <tt>last_modified_on</tt>
21
+ via <tt>named_scope_for_time_attr</tt>:
22
+
23
+ class Claim < ActiveRecord::Base
24
+ named_scope_for_time_attr :created_at, :order => 'created_at DESC'
25
+ named_scope_for_time_attr :last_modified_on, :limit => 10
26
+ ...
27
+ end
28
+
29
+ It is then possible to use the scope method <tt>created_at</tt> and <tt>updated_at</tt>
30
+ as follows:
31
+
32
+ Claim.created_at(2.days.ago)
33
+ Claim.created_at(2.days.ago, 1.days.ago)
34
+ Claim.created_at(:today)
35
+ Claim.created_at(:this_month).order('created_at DESC')
36
+ Claim.created_at(:q1) # quarter one
37
+ Claim.last_modified_on(3.weeks.ago, :limit => 20)
38
+ ...
39
+
40
+ See API doc for class <tt>named_scope_for_time_attr</tt> for examples:
41
+
42
+ == REQUIREMENTS:
43
+
44
+ * ActiveRecord
45
+
46
+ == INSTALL:
47
+
48
+ * sudo gem install named_scope_for_time_attr
49
+
50
+ == LICENSE:
51
+
52
+ (The MIT License)
53
+
54
+ Copyright (c) 2009-* Thomas Baustert
55
+
56
+ Permission is hereby granted, free of charge, to any person obtaining
57
+ a copy of this software and associated documentation files (the
58
+ 'Software'), to deal in the Software without restriction, including
59
+ without limitation the rights to use, copy, modify, merge, publish,
60
+ distribute, sublicense, and/or sell copies of the Software, and to
61
+ permit persons to whom the Software is furnished to do so, subject to
62
+ the following conditions:
63
+
64
+ The above copyright notice and this permission notice shall be
65
+ included in all copies or substantial portions of the Software.
66
+
67
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
68
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
69
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
70
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
71
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
72
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
73
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/named_scope_for_time_attr'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('named_scope_for_time_attr', NamedScopeForTimeAttr::VERSION) do |p|
7
+ p.developer('Thomas Baustert', 'business@thomasbaustert.de')
8
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
9
+ p.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
10
+ p.rubyforge_name = "nsfta"
11
+ # p.extra_deps = [
12
+ # ['activesupport','>= 2.0.2'],
13
+ # ]
14
+ p.extra_dev_deps = [
15
+ ['newgem', ">= #{::Newgem::VERSION}"]
16
+ ]
17
+
18
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
19
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
20
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
21
+ p.rsync_args = '-av --delete --ignore-errors'
22
+ end
23
+
24
+ require 'newgem/tasks' # load /tasks/*.rake
25
+ Dir['tasks/**/*.rake'].each { |t| load t }
26
+
27
+ # TODO - want other tests/tasks run by default? Add them to the list
28
+ # task :default => [:spec, :features]
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ # Diese Datei macht aus dem Gem ein Rails Plugin
2
+ require 'named_scope_for_time_attr'
@@ -0,0 +1,15 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'active_record'
5
+
6
+ module NamedScopeForTimeAttr
7
+ VERSION = '1.0.0'
8
+ end
9
+
10
+ require File.expand_path(File.dirname(__FILE__) + '/named_scope_for_time_attr/named_scope_for_time_attr')
11
+
12
+ # include the method in ActiveRecord::Base so it is available in all models by default
13
+ ActiveRecord::Base.class_eval do
14
+ include NamedScopeForTimeAttr
15
+ end
@@ -0,0 +1,162 @@
1
+ module NamedScopeForTimeAttr #:nodoc:
2
+
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+
9
+ ##
10
+ # Provides the <tt>ActiveRecord</tt> helper method <tt>named_scope_for_time_attr</tt>.
11
+ # Given the name of a time attribute of an ActiveRecord model
12
+ # (e.g. <tt>created_at</tt> of <tt>Claim</tt>) it defines a correspondent
13
+ # named scope for easy access to an arbitrarily time range (scope).
14
+ #
15
+ # ==== Examples
16
+ #
17
+ # class Claim < ActiveRecord::Base
18
+ # named_scope_for_time_attr :created_at, :order => 'created_at DESC'
19
+ # named_scope_for_time_attr :updated_at
20
+ # named_scope_for_time_attr :last_modified_on, :limit => 10
21
+ # ...
22
+ # end
23
+ #
24
+ # It is then possible to use the scope method <tt>created_at</tt> and <tt>updated_at</tt>
25
+ # as follows:
26
+ #
27
+ # Claim.created_at(2.days.ago)
28
+ # Claim.updated_at(2.days.ago, 1.days.ago)
29
+ # Claim.last_modified_on(3.weeks.ago)
30
+ #
31
+ # Is is even possible to use one of the following predefined keys to specify a time range:
32
+ #
33
+ # * <tt>:today</tt>
34
+ # * <tt>:yesterday</tt>
35
+ # * <tt>:tomorrow</tt>
36
+ # * <tt>:this_week</tt>
37
+ # * <tt>:last_week</tt>
38
+ # * <tt>:next_week</tt>
39
+ # * <tt>:this_month</tt>
40
+ # * <tt>:last_month</tt>
41
+ # * <tt>:next_month</tt>
42
+ # * <tt>:this_quarter</tt>
43
+ # * <tt>:last_quarter</tt>
44
+ # * <tt>:next_quarter</tt>
45
+ # * <tt>:q1</tt>
46
+ # * <tt>:q2</tt>
47
+ # * <tt>:q3</tt>
48
+ # * <tt>:q4</tt>
49
+ # * <tt>:this_year</tt>
50
+ # * <tt>:last_year</tt>
51
+ # * <tt>:next_year</tt>
52
+ #
53
+ # And use them as follows:
54
+ #
55
+ # Claim.created_at(:today)
56
+ # Claim.updated_at(:this_month)
57
+ # Claim.last_modified_on(:last_year)
58
+ # Claim.created_at(:q1) # quarter one
59
+ # Claim.created_at(:this_quarter)
60
+ #
61
+ # One can specify additional conditional options to <tt>named_scope_for_time_attr</tt>
62
+ # to sort or limit the list of object loaded:
63
+ #
64
+ # class Claim < ActiveRecord::Base
65
+ # named_scope_for_time_attr :created_at, :order => 'created_at DESC'
66
+ # named_scope_for_time_attr :updated_at, :limit => 10
67
+ # ...
68
+ # end
69
+ #
70
+ # The definition also provide a scope <tt>order</tt> to ordered the list which is used
71
+ # as follows:
72
+ #
73
+ # Claim.updated_at(:yesterday).order(:updated_at) # => ... ORDER BY created_at
74
+ # Claim.updated_at(:last_week).order('updated_at DESC') # => ... ORDER BY updated_at DESC
75
+ # Claim.updated_at(:yesterday).order('shortname') # => ... ORDER BY shortname
76
+ #
77
+ def named_scope_for_time_attr(attr_name, options = {})
78
+ named_scope attr_name, lambda { |*args| build_conditions("#{table_name}.#{attr_name}", *args).merge(options) }
79
+ named_scope :order, lambda { |*args| {:order => (args.first || "#{attr_name} DESC") } }
80
+ end
81
+
82
+ private
83
+
84
+ QUARTER_KEYS = [:this_quarter, :last_quarter, :next_quarter, :q1, :q2, :q3, :q4].freeze
85
+ QUARTER_MONTHS = { 1 => [1,3], 2 => [4,6], 3 => [7,9], 4 => [10,12] }.freeze
86
+ MAP_TIME = {
87
+ :today => [ 0, :day],
88
+ :yesterday => [ 1, :day],
89
+ :tomorrow => [-1, :day],
90
+ :this_week => [ 0, :week],
91
+ :last_week => [ 1, :week],
92
+ :next_week => [-1, :week],
93
+ :this_month => [ 0, :month],
94
+ :last_month => [ 1, :month],
95
+ :next_month => [-1, :month],
96
+ :this_year => [ 0, :year],
97
+ :last_year => [ 1, :year],
98
+ :next_year => [-1, :year]
99
+ }
100
+
101
+ ##
102
+ # Examples:
103
+ # build_conditions(:created_at, :last_week)
104
+ # build_conditions(:created_at, 2.days.ago, 1.days.ago)
105
+ # build_conditions(:created_at, 3.hours.ago)
106
+ #
107
+ def build_conditions(attr_name, *args)
108
+ if QUARTER_KEYS.include?(args.first)
109
+ quarter_range_conditions(attr_name, args.first)
110
+ elsif args.first.kind_of?(Symbol)
111
+ count, key = MAP_TIME[args.first]
112
+ time_range_conditions_ago(attr_name, count || 0, key || args.first)
113
+ elsif args.size == 2
114
+ time_range_conditions(attr_name, args.first, args.last)
115
+ else
116
+ {:conditions => ["#{attr_name} >= ?", args.first]}
117
+ end
118
+ end
119
+
120
+ ##
121
+ # Examples:
122
+ # time_range_conditions_ago(:created_at, 0, :day)
123
+ # time_range_conditions_ago(:created_at, 1, :week)
124
+ #
125
+ def time_range_conditions_ago(attr_name, count, key)
126
+ eval("time_range_conditions(attr_name, #{count}.#{key}.ago.beginning_of_#{key}, #{count}.#{key}.ago.end_of_#{key})")
127
+ end
128
+
129
+ def time_range_conditions(attr_name, start_time, end_time)
130
+ {:conditions => ["#{attr_name} >= ? AND #{attr_name} <= ?", start_time, end_time]}
131
+ end
132
+
133
+ def quarter_range_conditions(attr_name, quarter)
134
+ year = Time.now.year
135
+ if quarter.to_s =~ /^q\d{1}/
136
+ quarter = quarter.to_s[1,1].to_i
137
+ else
138
+ month = Time.now.month
139
+ q = #TODO/2009-03-02/tb better code
140
+ if month < 4
141
+ 1
142
+ elsif month < 7
143
+ 2
144
+ elsif month < 10
145
+ 3
146
+ else
147
+ 4
148
+ end
149
+ quarter = q if quarter == :this_quarter
150
+ quarter = q-1 if quarter == :last_quarter
151
+ quarter = q+1 if quarter == :next_quarter
152
+ quarter > 4 ? 1 : (quarter < 1 ? 4 : quarter)
153
+ end
154
+ start_month, end_month = QUARTER_MONTHS[quarter]
155
+ time_range_conditions(attr_name,
156
+ Time.mktime(year, start_month).beginning_of_month,
157
+ Time.mktime(year, end_month).end_of_month)
158
+ end
159
+
160
+ end
161
+ end
162
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,4 @@
1
+ require 'test/unit'
2
+ require 'rubygems' #needed for run tests in TextMate
3
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/named_scope_for_time_attr')
4
+
@@ -0,0 +1,111 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ # The ActiveRecord test model.
4
+ # Note: we don't need a database as long as we only build named scopes.
5
+ class Claim < ActiveRecord::Base
6
+ named_scope_for_time_attr :created_at
7
+ named_scope_for_time_attr :updated_at, :order => 'name DESC'
8
+ end
9
+
10
+ # Simulate a constant time.
11
+ # You can use mocha for this but than you have one dependency more.
12
+ class Time
13
+ def self.now
14
+ Time.mktime(2009,3,1,12,0,0)
15
+ end
16
+ end
17
+
18
+ class TestNamedScopeForTimeAttr < Test::Unit::TestCase
19
+
20
+ # #TODO/2009-03-02/tb
21
+ # tests gehen nicht auf die DB, d.h. das Statement wird nicht wirklich ausgeführt. ist das gut?
22
+
23
+ def test_scope_with_today
24
+ assert_conditions("2009-03-01 00:00:00", "2009-03-01 23:59:59", :today)
25
+ end
26
+
27
+ def test_scope_with_yesterday
28
+ assert_conditions("2009-02-28 00:00:00", "2009-02-28 23:59:59", :yesterday)
29
+ end
30
+
31
+ def test_scope_with_tomorrow
32
+ assert_conditions("2009-03-02 00:00:00", "2009-03-02 23:59:59", :tomorrow)
33
+ end
34
+
35
+ def test_scope_with_this_week
36
+ assert_conditions("2009-02-23 00:00:00", "2009-03-01 23:59:59", :this_week)
37
+ end
38
+
39
+ def test_scope_with_last_week
40
+ assert_conditions("2009-02-16 16 00:00:00", "2009-02-22 23:59:59", :last_week)
41
+ end
42
+
43
+ def test_scope_with_next_week
44
+ assert_conditions("2009-03-02 00:00:00", "2009-03-08 23:59:59", :next_week)
45
+ end
46
+
47
+ def test_scope_with_this_month
48
+ assert_conditions("2009-03-01 00:00:00", "2009-03-31 23:59:59", :this_month)
49
+ end
50
+
51
+ def test_scope_with_last_month
52
+ assert_conditions("2009-02-01 00:00:00", "2009-02-28 23:59:59", :last_month)
53
+ end
54
+
55
+ def test_scope_with_next_month
56
+ assert_conditions("2009-04-01 00:00:00", "2009-04-30 23:59:59", :next_month)
57
+ end
58
+
59
+ def test_scope_with_this_quarter
60
+ assert_conditions("2009-01-01 00:00:00", "2009-03-31 23:59:59", :this_quarter)
61
+ end
62
+
63
+ def test_scope_with_q1
64
+ assert_conditions("2009-01-01 00:00:00", "2009-03-31 23:59:59", :q1)
65
+ end
66
+
67
+ def test_scope_with_q2
68
+ assert_conditions("2009-04-01 00:00:00", "2009-06-30 23:59:59", :q2)
69
+ end
70
+
71
+ def test_scope_with_q3
72
+ assert_conditions("2009-07-01 00:00:00", "2009-09-30 23:59:59", :q3)
73
+ end
74
+
75
+ def test_scope_with_q4
76
+ assert_conditions("2009-10-01 00:00:00", "2009-12-31 23:59:59", :q4)
77
+ end
78
+
79
+ def test_scope_with_this_year
80
+ assert_conditions("2009-01-01 00:00:00", "2009-12-31 23:59:59", :this_year)
81
+ end
82
+
83
+ def test_scope_with_last_year
84
+ assert_conditions("2008-01-01 00:00:00", "2008-12-31 23:59:59", :last_year)
85
+ end
86
+
87
+ def test_scope_with_next_year
88
+ assert_conditions("2010-01-01 00:00:00", "2010-12-31 23:59:59", :next_year)
89
+ end
90
+
91
+ def test_scope_for_created_at_without_order
92
+ assert_nil Claim.created_at(:today).proxy_options[:order]
93
+ end
94
+
95
+ def test_scope_for_updated_at_with_order
96
+ assert_equal "name DESC", Claim.updated_at(:today).proxy_options[:order]
97
+ end
98
+
99
+ private
100
+
101
+ def assert_conditions(start_time, end_time, time_key)
102
+ assert_equal expected_options(start_time, end_time), Claim.created_at(time_key).proxy_options
103
+ end
104
+
105
+ def expected_options(start_time, end_time)
106
+ {:conditions => ["claims.created_at >= ? AND claims.created_at <= ?",
107
+ Time.parse(start_time), Time.parse(end_time)]}
108
+ end
109
+ end
110
+
111
+
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: named_scope_for_time_attr
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Thomas Baustert
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-11 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: newgem
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.0
34
+ version:
35
+ description: Provides the ActiveRecord helper method <tt>named_scope_for_time_attr</tt>. Given the name of a time attribute of an ActiveRecord model (e.g. <tt>created_at</tt> of <tt>Claim</tt>) it defines a correspondent named scope for easy access to an arbitrarily time range (scope).
36
+ email:
37
+ - business@thomasbaustert.de
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - History.txt
44
+ - Manifest.txt
45
+ - PostInstall.txt
46
+ - README.rdoc
47
+ files:
48
+ - History.txt
49
+ - Manifest.txt
50
+ - PostInstall.txt
51
+ - README.rdoc
52
+ - Rakefile
53
+ - init.rb
54
+ - lib/named_scope_for_time_attr.rb
55
+ - lib/named_scope_for_time_attr/named_scope_for_time_attr.rb
56
+ - script/destroy
57
+ - script/generate
58
+ - test/test_helper.rb
59
+ - test/test_named_scope_for_time_attr.rb
60
+ has_rdoc: true
61
+ homepage: "All information can be found here: http://nsfta.rubyforge.org/"
62
+ post_install_message: PostInstall.txt
63
+ rdoc_options:
64
+ - --main
65
+ - README.rdoc
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ requirements: []
81
+
82
+ rubyforge_project: nsfta
83
+ rubygems_version: 1.3.1
84
+ signing_key:
85
+ specification_version: 2
86
+ summary: Provides the ActiveRecord helper method <tt>named_scope_for_time_attr</tt>
87
+ test_files:
88
+ - test/test_helper.rb
89
+ - test/test_named_scope_for_time_attr.rb