date_scopes 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .yardoc
5
+ doc
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm gemset use date_scopes
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in date_scope.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'autowatchr', :git => 'https://github.com/SFEley/autowatchr.git'
8
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,54 @@
1
+ GIT
2
+ remote: https://github.com/SFEley/autowatchr.git
3
+ revision: 2b2d80e0e8f4075d278adbb1373cad854d35a454
4
+ specs:
5
+ autowatchr (0.1.4)
6
+ watchr
7
+
8
+ PATH
9
+ remote: .
10
+ specs:
11
+ date_scopes (0.0.1)
12
+ activerecord (~> 3)
13
+
14
+ GEM
15
+ remote: http://rubygems.org/
16
+ specs:
17
+ activemodel (3.0.3)
18
+ activesupport (= 3.0.3)
19
+ builder (~> 2.1.2)
20
+ i18n (~> 0.4)
21
+ activerecord (3.0.3)
22
+ activemodel (= 3.0.3)
23
+ activesupport (= 3.0.3)
24
+ arel (~> 2.0.2)
25
+ tzinfo (~> 0.3.23)
26
+ activesupport (3.0.3)
27
+ arel (2.0.6)
28
+ builder (2.1.2)
29
+ diff-lcs (1.1.2)
30
+ i18n (0.5.0)
31
+ rspec (2.2.0)
32
+ rspec-core (~> 2.2)
33
+ rspec-expectations (~> 2.2)
34
+ rspec-mocks (~> 2.2)
35
+ rspec-core (2.2.1)
36
+ rspec-expectations (2.2.0)
37
+ diff-lcs (~> 1.1.2)
38
+ rspec-mocks (2.2.0)
39
+ sqlite3-ruby (1.3.2)
40
+ timecop (0.3.5)
41
+ tzinfo (0.3.23)
42
+ watchr (0.7)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ activerecord (~> 3)
49
+ autowatchr!
50
+ bundler (>= 1.0.0)
51
+ date_scopes!
52
+ rspec (~> 2)
53
+ sqlite3-ruby
54
+ timecop
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 ThroughTheNet
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,97 @@
1
+ #Date_Scopes - an ActiveRecord extension for automatic date-based scopes
2
+
3
+ **Homepage/Github**: [github.com/ThroughTheNet/date_scopes](https://github.com/ThroughTheNet/date_scopes)
4
+ **Documentation**: [rubydoc.info](http://rubydoc.info/gems/date_scopes/0.1/frames)
5
+ **Author**: Jonathan Davies ([ThroughTheNet](http://throughthnet.com))
6
+ **Contact**: [info@throughthenet.com](mailto:info@throughthenet.com)
7
+ **License**: [MIT License](http://opensource.org/licenses/mit-license.php)
8
+ **Version**: 0.1
9
+ **Released**: December 2nd 2010
10
+
11
+ ##Description
12
+
13
+
14
+ Date_Scopes is a rubygem that adds a simple macro, {DateScopes::ClassMethods#has_date_scopes has_date_scopes} to ActiveRecord. When used it adds a number of convinience scopes to your models relating to a whether a particular date field on that model is in the past or future. It also has other handy features.
15
+
16
+ Note that it does *not* depend on the whole of Rails, but is a pure ActiveRecord extension. As such it can be used wherever you use ActiveRecord, be it in Sinatra or whatever other mad thing you've cooked up.
17
+
18
+ ##Example Use Case
19
+
20
+ Say you have a model `Post` in your blog app. You want to be able to write your posts and save them, but not have them appear on the site until a certain time in the future, when they should automatically appear.
21
+ Perhaps you have offers or coupons that need to expire after a certain date, or maybe some task has a deadline by which it must be completed.
22
+ This gem will neaten up your model code whenever your model instances need to be divided into to groups: those of them whos date field is before `Time.now`, and those whos date field is after `Time.now`
23
+
24
+ ##Functionality
25
+
26
+ ###Dynamic Scopes
27
+
28
+ Assuming that the model has a datetime field called `published_at`, one can simply call the {DateScopes::ClassMethods#has_date_scopes has_date_scopes} method in the model defintion like so:
29
+
30
+ class Post < ActiveRecord::Base
31
+ has_date_scopes
32
+ end
33
+
34
+ This will create the following scopes on the post class
35
+
36
+ published
37
+
38
+ unpublished
39
+ nonpublished
40
+ non_published
41
+ not_published
42
+
43
+ The first scope will return all `Post` records whose `published_at` field is in the past.
44
+ All the other scopes will return the `Post` records whose `published_at` field is in the future.
45
+ The duplication of the negative scopes is just in the interests of natural looking code.
46
+ Note that any records whose `published_at` field exactly equals `Time.now` will fall into both scopes.
47
+ Any record with a `nil` `published_at` field will be in the negative scopes, `unpublished` and so on.
48
+
49
+ ###Virtual Accessors
50
+
51
+ Use of the macro also creates an automatic virtual getter/setter pair, allowing you to interact with the `published_at` column as if it were a boolean column called `published` in that it defines the following methods:
52
+
53
+ published
54
+ published?
55
+
56
+ published=
57
+
58
+ The first two simply return true or false depending on whether the field is in the past or future, much the same as the scopes described above. The second is more interesting, in that it accepts a boolean.
59
+ If it is passed `true`, it will set the `published_at` column to `Time.now`. If it is passed `false` it sets `published_at` to `nil`. Again it is obvious how this complements the scopes.
60
+
61
+ These virtual boolean setters/getters function exactly like real ones, so feel free to use them in your forms and such, in exactly the same way as you would as if you had a real boolean column in your schema.
62
+ This can be used to keep a record of when a user set a boolean as true, for example if your date column is `deleted_at` you could use the `deleted=` virtual setter in a form so the user can 'delete' the record easily, and you will have a record in the `deleted_at` column of when this change took place.
63
+ Obviously one could then use the `non_deleted` scope to hide those records from the index action etc.
64
+
65
+ ##Customization
66
+
67
+ The only option accepted by {DateScopes::ClassMethods#has_date_scopes has_date_scopes} currently is `column` which simply changes the field name used and the corresponding dynamic scopes/setters/getters from its default of `:published`.
68
+ For example if one had a model `Order` with a `DateTime` column called `filled_at` one could define it like this:
69
+
70
+ class Order < ActiveRecord::Base
71
+ has_date_scopes :column => :filled
72
+ end
73
+
74
+ Then the `Order` class would have the following scopes:
75
+
76
+ filled
77
+
78
+ unfilled
79
+ nonfilled
80
+ non_published
81
+ not_published
82
+
83
+ **NOTE**: The column option actually takes the verb that will form part of the various dynamic methods, and then adds `_at` to that name to find the name of the database column. In future it should be made to accept either but the current interface will not be broken. If the correct database column for the `:column` option is not found, the macro method will raise an exception.
84
+
85
+ ##Contributing
86
+
87
+ This it github. You know the score!
88
+
89
+ 1. Fork
90
+ 2. Hack
91
+ 3. Push
92
+ 4. Pull
93
+
94
+ Tests are set up with Rspec2, `bundle exec watchr spec.watchr` is reccommended for continous testing goodness.
95
+
96
+ ## Copyright
97
+ Copyright (c) 2010 ThroughTheNet. MIT Licensed. See the {file:LICENSE} file
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/date_scopes/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "date_scopes"
6
+ s.version = DateScopes::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Jonathan Davies', 'ThroughTheNet']
9
+ s.email = ['info@throughthenet.com']
10
+ s.license = "MIT"
11
+ s.homepage = "https://github.com/ThroughTheNet/date_scopes"
12
+ s.summary = "An ActiveRecord extension for automatic date-based scopes"
13
+ s.description = "Adds a simple macro, has_date_scopes to ActiveRecord. When used it adds a number of convinience scopes to your models relating to a whether a particular date field on that model is in the past or future. It also has other handy features."
14
+
15
+ s.required_rubygems_version = ">= 1.3.6"
16
+
17
+ s.add_dependency "activerecord", "~> 3"
18
+
19
+ s.add_development_dependency "bundler", ">= 1.0.0"
20
+ s.add_development_dependency "rspec", "~> 2"
21
+ s.add_development_dependency 'timecop', '>= 0'
22
+ s.add_development_dependency 'sqlite3-ruby', '>= 0'
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
26
+ s.require_path = 'lib'
27
+ end
@@ -0,0 +1,69 @@
1
+ require 'active_record'
2
+
3
+ module DateScopes
4
+
5
+ module ClassMethods
6
+
7
+ # Adds a number of dynamic scopes and a virtual accessor to your model.
8
+ # Functionality is detailed more fully in the {file:README.markdown README} for this gem, but a brief demo is given here:
9
+ # @example
10
+ # class Post < ActiveRecord::Base
11
+ # has_date_scopes
12
+ # end
13
+ #
14
+ # Post.published #posts with published_at before Time.now
15
+ # Post.unpublished #posts with published_at after Time.now
16
+ # post = Post.new
17
+ # post.published = true #virtual setter, sets published_at to Time.now
18
+ # post.published_at == Time.now #true
19
+ # post.published? == true #true
20
+ # post.published = false #virtual setter, sets published_at to nil
21
+ # post.published_at.nil? #true
22
+ # @param [Hash] opts The options
23
+ # @option opts [Symbol, String] :column The verb that will be used to form the names of the scopes, and corresponds to a database column (if :column is `deleted` then there must be a database column `deleted_at`.) Defaults to `published` and a corresponding database column `published_at`
24
+ # @raise [ActiveRecordError] An exception will be raised if the required column is not present, telling you the name of the column it expects.
25
+
26
+ def has_date_scopes(options = {})
27
+ options.to_options!.reverse_merge! :column => 'published'
28
+ column_name = "#{options[:column]}_at"
29
+
30
+ raise new ActiveRecordError("Could not find the #{column_name} column on the #{table_name} table") unless column_names.include? column_name
31
+
32
+ on_scope_sql = "#{table_name}.#{column_name} <= ?"
33
+
34
+ un_scope_sql = "#{table_name}.#{column_name} >= ? OR #{table_name}.#{column_name} IS NULL"
35
+
36
+ scope options[:column], lambda { where(on_scope_sql, Time.now) }
37
+ %w{un non non_ not_}.each do |prefix|
38
+ scope prefix+options[:column].to_s, lambda { where(un_scope_sql, Time.now) }
39
+ end
40
+
41
+ define_method options[:column].to_s+'=' do |value|
42
+ if value && !self[column_name]
43
+
44
+ self[column_name] = Time.now
45
+
46
+ elsif !value
47
+
48
+ self[column_name] = nil
49
+ end
50
+ end
51
+
52
+ define_method options[:column] do
53
+ return false unless time = self[column_name]
54
+ time <= Time.now
55
+ end
56
+ alias_method options[:column].to_s+'?', options[:column]
57
+
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def self.included(base)
64
+ base.extend(ClassMethods)
65
+ end
66
+
67
+ end
68
+
69
+ ActiveRecord::Base.send(:include, DateScopes)
@@ -0,0 +1,3 @@
1
+ module DateScopes
2
+ VERSION = "0.1"
3
+ end
data/spec.watchr ADDED
@@ -0,0 +1,8 @@
1
+ require 'stringio'
2
+ require 'autowatchr'
3
+
4
+ Autowatchr.new(self) do |config|
5
+ config.test_dir = 'spec'
6
+ config.test_re = "^spec/.*_spec\\.rb$"
7
+ config.test_file = '%s_spec.rb'
8
+ end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ class Post < ActiveRecord::Base;end
4
+ class Page < ActiveRecord::Base;end
5
+ class Empty < ActiveRecord::Base;end
6
+
7
+ describe DateScopes do
8
+
9
+ context 'with default options' do
10
+ before(:all) {Post.class_exec {has_date_scopes}}
11
+
12
+ it 'has un-scopes' do
13
+ Post.scopes.should include(:unpublished)
14
+ Post.scopes.should include(:nonpublished)
15
+ Post.scopes.should include(:non_published)
16
+ Post.scopes.should include(:not_published)
17
+ end
18
+
19
+ it 'has on-scope' do
20
+ Post.scopes.should include(:published)
21
+ end
22
+
23
+ it "accepts true to boolean setter and sets as the time" do
24
+ Timecop.freeze do
25
+ post = Post.new
26
+ post.published = true
27
+ post.published_at.should == Time.now
28
+ end
29
+ end
30
+
31
+ it "doesn't reset the time with a boolean when time is already set" do
32
+ Timecop.freeze
33
+ post = Post.new
34
+ post.published = true
35
+ post.save
36
+ time = post.published_at
37
+ Timecop.return
38
+ post.update_attributes :published => true
39
+ time.should == post.published_at
40
+ end
41
+
42
+ it "accepts false to boolean setter and sets column as nil" do
43
+ post = Post.new
44
+ post.published_at = Time.now
45
+ post.published_at.should_not be_nil
46
+ post.published = false
47
+ post.published_at.should be_nil
48
+ end
49
+
50
+ context 'when published_at is in the past' do
51
+ before(:all) {@post = Post.create(:published_at => Time.now - 10.minutes)}
52
+
53
+ it 'is in the published scope' do
54
+ Post.published.should include(@post)
55
+ end
56
+
57
+ it 'is not in the unpublished scope' do
58
+ Post.unpublished.should_not include(@post)
59
+ end
60
+
61
+ it "is published" do
62
+ @post.should be_published
63
+ end
64
+
65
+ end
66
+
67
+ context 'when published_at is in the future' do
68
+ before(:all) {@post = Post.create :published_at => Time.now + 10.minutes}
69
+
70
+ it "is in the unpublished scope" do
71
+ Post.unpublished.should include(@post)
72
+ end
73
+
74
+ it "is not in the published scope" do
75
+ Post.published.should_not include(@post)
76
+ end
77
+
78
+ it "is not published" do
79
+ @post.should_not be_published
80
+ end
81
+
82
+ end
83
+
84
+ context 'when published_at is empty' do
85
+ before(:all) {@post = Post.create(:published_at => nil)}
86
+
87
+ it "is in the unpublished scope" do
88
+ Post.unpublished.should include(@post)
89
+ end
90
+
91
+ it "is not in the published scope" do
92
+ Post.published.should_not include(@post)
93
+ end
94
+
95
+ it "is not published" do
96
+ @post.should_not be_published
97
+ end
98
+ end
99
+
100
+ context 'with custom column name' do
101
+ before(:all) {Page.class_exec{has_date_scopes :column => :sold}}
102
+
103
+ it 'has un-scopes' do
104
+ Page.scopes.should include(:unsold)
105
+ Page.scopes.should include(:nonsold)
106
+ Page.scopes.should include(:non_sold)
107
+ Page.scopes.should include(:not_sold)
108
+ end
109
+
110
+ it 'has on-scope' do
111
+ Page.scopes.should include(:sold)
112
+ end
113
+ end
114
+
115
+ context 'when missing the required column' do
116
+ it "raises an exception" do
117
+ lambda {Empty.class_exec{has_date_scopes(:column => :foobar)}}.should raise_error
118
+ end
119
+ end
120
+ end
121
+
122
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ ENV["RAILS_ENV"] ||= 'test'
5
+ Bundler.require(:default, :development)
6
+
7
+ ActiveRecord::Base.establish_connection(
8
+ :adapter => 'sqlite3',
9
+ :database => 'spec/support/test.sqlite3'
10
+ )
Binary file
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: date_scopes
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Jonathan Davies
13
+ - ThroughTheNet
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-12-02 00:00:00 +00:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activerecord
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 5
30
+ segments:
31
+ - 3
32
+ version: "3"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: bundler
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 23
44
+ segments:
45
+ - 1
46
+ - 0
47
+ - 0
48
+ version: 1.0.0
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: rspec
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ hash: 7
60
+ segments:
61
+ - 2
62
+ version: "2"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: timecop
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :development
78
+ version_requirements: *id004
79
+ - !ruby/object:Gem::Dependency
80
+ name: sqlite3-ruby
81
+ prerelease: false
82
+ requirement: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ type: :development
92
+ version_requirements: *id005
93
+ description: Adds a simple macro, has_date_scopes to ActiveRecord. When used it adds a number of convinience scopes to your models relating to a whether a particular date field on that model is in the past or future. It also has other handy features.
94
+ email:
95
+ - info@throughthenet.com
96
+ executables: []
97
+
98
+ extensions: []
99
+
100
+ extra_rdoc_files: []
101
+
102
+ files:
103
+ - .gitignore
104
+ - .rspec
105
+ - .rvmrc
106
+ - Gemfile
107
+ - Gemfile.lock
108
+ - LICENSE
109
+ - README.markdown
110
+ - Rakefile
111
+ - date_scopes.gemspec
112
+ - lib/date_scopes.rb
113
+ - lib/date_scopes/version.rb
114
+ - spec.watchr
115
+ - spec/date_scopes_spec.rb
116
+ - spec/spec_helper.rb
117
+ - spec/support/test.sqlite3
118
+ has_rdoc: true
119
+ homepage: https://github.com/ThroughTheNet/date_scopes
120
+ licenses:
121
+ - MIT
122
+ post_install_message:
123
+ rdoc_options: []
124
+
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ hash: 3
133
+ segments:
134
+ - 0
135
+ version: "0"
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ hash: 23
142
+ segments:
143
+ - 1
144
+ - 3
145
+ - 6
146
+ version: 1.3.6
147
+ requirements: []
148
+
149
+ rubyforge_project:
150
+ rubygems_version: 1.3.7
151
+ signing_key:
152
+ specification_version: 3
153
+ summary: An ActiveRecord extension for automatic date-based scopes
154
+ test_files: []
155
+