Fingertips-as_new-san 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +6 -0
- data/VERSION.yml +4 -0
- data/lib/as_new_san.rb +120 -0
- data/test/as_new_san_test.rb +32 -0
- data/test/test_helper.rb +50 -0
- metadata +60 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright © 2008 Fingertips, Eloy Duran <eloy.de.enige@gmail.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/VERSION.yml
ADDED
data/lib/as_new_san.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# The AsNewSan mixin makes it easier to create associations on a new Active
|
2
|
+
# Record instance.
|
3
|
+
#
|
4
|
+
# Use the +as_new+ method to instantiate new empty objects that are immediately
|
5
|
+
# saved to the database with a special flag marking them as new. Because new
|
6
|
+
# instances are already stored in the database, you always have an +id+
|
7
|
+
# available for creating associations. This means you can use the same views
|
8
|
+
# and controller logic for new and edit actions which is especially helpful
|
9
|
+
# when you are creating new associated objects using Ajax calls.
|
10
|
+
#
|
11
|
+
# An Active Record class using this mixin needs to have an +as_new+ boolean
|
12
|
+
# column with the default value set to +false+ and a +created_at+ column.
|
13
|
+
#
|
14
|
+
# See AsNewSan::ClassMethods for documentation on the class methods added to
|
15
|
+
# ActiveRecord::Base.
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
#
|
19
|
+
# class CreateMessages < ActiveRecord::Migration
|
20
|
+
# def self.up
|
21
|
+
# create_table :messages do |t|
|
22
|
+
# t.string :body
|
23
|
+
# t.boolean :as_new, :default => false
|
24
|
+
# t.timestamps
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# class CreateRecipients < ActiveRecord::Migration
|
30
|
+
# def self.up
|
31
|
+
# create_table :recipient do |t|
|
32
|
+
# t.integer :message_id
|
33
|
+
# t.string :name
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# class Message < ActiveRecord::Base
|
39
|
+
# include AsNewSan
|
40
|
+
# has_many :recipients, :dependent => :destroy
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# class Recipient < ActiveRecord::Base
|
44
|
+
# belongs_to :message
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# class MessagesController < ApplicationController
|
48
|
+
# def index
|
49
|
+
# # Remove all records older than 1 week that have the `as_new` property set to `true`.
|
50
|
+
# Message.collect_garbage!
|
51
|
+
#
|
52
|
+
# # The mixin comes with the `find_without_as_new` class method, which behaves as the
|
53
|
+
# # ActiveRecord::Base#find class method, but only finds records which have their
|
54
|
+
# # `as_new` property set to `false`.
|
55
|
+
# @messages = Message.find_without_as_new(:all)
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# def new
|
59
|
+
# # Create and save a message with the `as_new` property set to `true`.
|
60
|
+
# #
|
61
|
+
# # Recipients are created and associated using Ajax calls on the recipients controller.
|
62
|
+
# @message = Message.as_new
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# def update
|
66
|
+
# # The mixin comes with a `before_update` filter that sets the `as_new` column to
|
67
|
+
# # `false` before saving the updated record.
|
68
|
+
# @message = Message.find(params[:id])
|
69
|
+
# @message.update_attributes(params[:message])
|
70
|
+
#
|
71
|
+
# # So, at this point the record is not marked `as_new` anymore.
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
module AsNewSan
|
75
|
+
def self.included(base) #:nodoc:
|
76
|
+
base.extend(ClassMethods)
|
77
|
+
base.before_update(:unset_as_new)
|
78
|
+
end
|
79
|
+
|
80
|
+
module ClassMethods
|
81
|
+
# Instantiates a new object, sets the as_new column to +true+ and saves the
|
82
|
+
# associated record without validation.
|
83
|
+
#
|
84
|
+
# If the record is never updated, or its as_new property explicitely set to
|
85
|
+
# +false+, it will be destroyed after 1 week when the collect_garbage!
|
86
|
+
# class method is called.
|
87
|
+
#
|
88
|
+
# See <tt>ActiveRecord::Base.new</tt> for all the other options that can be
|
89
|
+
# passed to as_new.
|
90
|
+
def as_new(attributes = {})
|
91
|
+
returning( new(attributes.merge( :as_new => true )) ) { |record| record.save(false) }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Finds and destroys all the records where the as_new column is set to
|
95
|
+
# +true+ and +created_at+ is longer than 1 week ago.
|
96
|
+
def collect_garbage!
|
97
|
+
find(:all, :conditions => ['as_new = ? AND created_at < ?', true, Time.now - 1.week]).each(&:destroy)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Works the same as <tt>ActiveRecord::Base.find</tt> but only returns
|
101
|
+
# records for which the as_new column is set to +false+.
|
102
|
+
def find_without_as_new(*args)
|
103
|
+
with_scope(:find => { :conditions => { :as_new => false } }) do
|
104
|
+
find(*args)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns a boolean indicating whether or not this is a +as_new+ record.
|
110
|
+
def as_new_record?
|
111
|
+
self.as_new
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def unset_as_new
|
117
|
+
self.as_new = false
|
118
|
+
true
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe "AsNewSan" do
|
4
|
+
include DBSetupAndTeardownHelper
|
5
|
+
|
6
|
+
it "should really create a record for a `new` object with `as_new` set to `true`" do
|
7
|
+
lambda { BaconFlavour.as_new(:name => 'chunky') }.should.differ('BaconFlavour.count', +1)
|
8
|
+
BaconFlavour.find_by_name('chunky').as_new.should.be true
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should garbage collect any record which has been in the db for a specific period" do
|
12
|
+
old_record = BaconFlavour.as_new(:name => 'banana', :created_at => (Time.now - 1.week - 1))
|
13
|
+
new_record = BaconFlavour.as_new(:name => 'smoked')
|
14
|
+
|
15
|
+
lambda { BaconFlavour.collect_garbage! }.should.differ('BaconFlavour.count', -1)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be possible to do a `find` without matching any `as_new` records" do
|
19
|
+
as_new_record = BaconFlavour.as_new(:name => 'smells as new')
|
20
|
+
not_as_new_record = BaconFlavour.create(:name => 'does not smell at all')
|
21
|
+
|
22
|
+
BaconFlavour.find(:all).should == [as_new_record, not_as_new_record]
|
23
|
+
BaconFlavour.find_without_as_new(:all).should == [not_as_new_record]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should set `as_new` records to `false` on update" do
|
27
|
+
record = BaconFlavour.as_new(:name => 'ice cream')
|
28
|
+
record.as_new_record?.should.be true
|
29
|
+
record.update_attribute(:name, 'that is right, bacon ice cream')
|
30
|
+
record.as_new_record?.should.be false
|
31
|
+
end
|
32
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "test/unit"
|
3
|
+
require "test/spec"
|
4
|
+
require 'activerecord'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
+
|
9
|
+
require File.expand_path('../../rails/init', __FILE__)
|
10
|
+
|
11
|
+
module DBSetupAndTeardownHelper
|
12
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
|
13
|
+
ActiveRecord::Migration.verbose = false
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.class_eval do
|
17
|
+
before do
|
18
|
+
ActiveRecord::Schema.define(:version => 1) do
|
19
|
+
create_table :bacon_flavours do |t|
|
20
|
+
t.string :name
|
21
|
+
t.boolean :as_new, :default => false
|
22
|
+
t.timestamps
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
29
|
+
ActiveRecord::Base.connection.drop_table(table)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class BaconFlavour < ActiveRecord::Base
|
37
|
+
include AsNewSan
|
38
|
+
end
|
39
|
+
|
40
|
+
module Test::Spec::Rails
|
41
|
+
module ShouldDiffer
|
42
|
+
def differ(eval_str, diff)
|
43
|
+
before = eval(eval_str)
|
44
|
+
@object.call
|
45
|
+
assert_equal before + diff, eval(eval_str)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Test::Spec::Should.send(:include, Test::Spec::Rails::ShouldDiffer)
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: Fingertips-as_new-san
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eloy Duran
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-06 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: The AsNewSan mixin makes it easier to create associations on a new Active Record instance. Use the as_new method to instantiate new empty objects that are immediately saved to the database with a special flag marking them as new. Because new instances are already stored in the database, you always have an id available for creating associations. This means you can use the same views and controller logic for new and edit actions which is especially helpful when you are creating new associated objects using Ajax calls.
|
17
|
+
email: eloy.de.enige@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
- LICENSE
|
25
|
+
files:
|
26
|
+
- README.rdoc
|
27
|
+
- VERSION.yml
|
28
|
+
- lib/as_new_san.rb
|
29
|
+
- test/as_new_san_test.rb
|
30
|
+
- test/test_helper.rb
|
31
|
+
- LICENSE
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://github.com/alloy/as_new_san
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options:
|
36
|
+
- --inline-source
|
37
|
+
- --charset=UTF-8
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
requirements: []
|
53
|
+
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 1.2.0
|
56
|
+
signing_key:
|
57
|
+
specification_version: 2
|
58
|
+
summary: A simple plugin which allows you to create records in the database, but treat them as if they were new records.
|
59
|
+
test_files: []
|
60
|
+
|