timestamper 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Manifest +14 -0
- data/README.rdoc +16 -0
- data/Rakefile +12 -0
- data/lib/timestamper.rb +6 -0
- data/lib/timestamper/activeresource.rb +21 -0
- data/lib/timestamper/configuration.rb +37 -0
- data/lib/timestamper/rack_middleware.rb +24 -0
- data/lib/timestamper/timestamper.rb +26 -0
- data/spec/fixtures.rb +8 -0
- data/spec/lib_specs/activeresource_spec.rb +42 -0
- data/spec/lib_specs/configuration_spec.rb +51 -0
- data/spec/lib_specs/rack_spec.rb +54 -0
- data/spec/lib_specs/timestamper_spec.rb +78 -0
- data/spec/spec_helper.rb +3 -0
- data/timestamper.gemspec +30 -0
- metadata +87 -0
data/Manifest
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
README.rdoc
|
2
|
+
Rakefile
|
3
|
+
lib/timestamper.rb
|
4
|
+
lib/timestamper/activeresource.rb
|
5
|
+
lib/timestamper/configuration.rb
|
6
|
+
lib/timestamper/rack_middleware.rb
|
7
|
+
lib/timestamper/timestamper.rb
|
8
|
+
spec/fixtures.rb
|
9
|
+
spec/lib_specs/activeresource_spec.rb
|
10
|
+
spec/lib_specs/configuration_spec.rb
|
11
|
+
spec/lib_specs/rack_spec.rb
|
12
|
+
spec/lib_specs/timestamper_spec.rb
|
13
|
+
spec/spec_helper.rb
|
14
|
+
Manifest
|
data/README.rdoc
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
= Use
|
2
|
+
|
3
|
+
Timestamper allows you to automatically set http response's header Last-Modified with value from ActiveResource or ActiveRecord. Currently only supports ActiveResource.
|
4
|
+
|
5
|
+
= Configuration
|
6
|
+
|
7
|
+
Timestamper.config do
|
8
|
+
updated_at_column :updated_at # global default column
|
9
|
+
end
|
10
|
+
|
11
|
+
class Article
|
12
|
+
timestamper_column :last_modified # custom timestamp column
|
13
|
+
end
|
14
|
+
|
15
|
+
To configure rack middleware in a Rails app, add following line to config/environment.rb.
|
16
|
+
ActionController::Dispatcher.middleware.use Timestamper::RackMiddleware
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('timestamper', '0.0.2') do |p|
|
6
|
+
p.description = "This gem sets Last-Modified in http response header with timestamp extracted from ActiveResource."
|
7
|
+
p.url = "http://github.com/dorren/timestamper"
|
8
|
+
p.author = "Dorren Chen"
|
9
|
+
p.email = "dorrenchen@gmail.com"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
data/lib/timestamper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveResource
|
2
|
+
class Base
|
3
|
+
class << self
|
4
|
+
def timestamper_column(column)
|
5
|
+
@timestamper_column = column
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
def new_with_timestamper(*args)
|
10
|
+
returning new_without_timestamper(*args) do |record|
|
11
|
+
next if !Timestamper.configuration.reportable?(record.class) # return triggers "LocalJumpError: unexpected return"
|
12
|
+
|
13
|
+
column = @timestamper_column || Timestamper.configuration.updated_at_column
|
14
|
+
Timestamper.instance.updated_at = record.send(column) if record.respond_to?(column)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method_chain :new, :timestamper
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Timestamper
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :updated_at_column
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@updated_at_column = :updated_at
|
7
|
+
@exclude = []
|
8
|
+
@only = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def exclude(*args)
|
12
|
+
@exclude += args
|
13
|
+
end
|
14
|
+
|
15
|
+
def excluded_models
|
16
|
+
@exclude.dup
|
17
|
+
end
|
18
|
+
|
19
|
+
def only(*args)
|
20
|
+
@only += args
|
21
|
+
end
|
22
|
+
|
23
|
+
def only_models
|
24
|
+
@only.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
def reportable?(klass)
|
28
|
+
return true if @exclude.empty? and @only.empty?
|
29
|
+
|
30
|
+
if @only.empty?
|
31
|
+
!@exclude.include?(klass)
|
32
|
+
else
|
33
|
+
(@only - @exclude).include?(klass)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Timestamper
|
2
|
+
class RackMiddleware
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
Timestamper.instance.reset
|
9
|
+
status, headers, response = @app.call(env)
|
10
|
+
if headers["Content-Type"].to_s =~ /^text\/html/
|
11
|
+
|
12
|
+
if status == 200 and headers['Last-Modified'].nil?
|
13
|
+
timestamp = Timestamper.instance.updated_at || Time.now
|
14
|
+
headers['Last-Modified'] = timestamp.httpdate
|
15
|
+
end
|
16
|
+
# rails return response object with body method, sinatra just returns array of 1 string
|
17
|
+
response = response.respond_to?(:body) ? response.body : response
|
18
|
+
[status, headers, response]
|
19
|
+
else
|
20
|
+
[status, headers, response]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
class Timestamper
|
3
|
+
include Singleton
|
4
|
+
attr_accessor :updated_at, :override
|
5
|
+
|
6
|
+
def reset
|
7
|
+
@override = nil
|
8
|
+
@updated_at = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def updated_at
|
12
|
+
@override ? @override : @updated_at
|
13
|
+
end
|
14
|
+
|
15
|
+
def updated_at=(time)
|
16
|
+
@updated_at = time if self.updated_at.nil? or time > self.updated_at
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.configuration
|
20
|
+
@configuration ||= Configuration.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.config(&block)
|
24
|
+
configuration.instance_eval(&block)
|
25
|
+
end
|
26
|
+
end
|
data/spec/fixtures.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveResource::Base do
|
4
|
+
before do
|
5
|
+
@today = Time.now
|
6
|
+
@yesterday = 1.day.ago
|
7
|
+
@tomorrow = 1.day.from_now
|
8
|
+
Timestamper.instance.reset
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#new" do
|
12
|
+
it "should report updated_at time" do
|
13
|
+
Timestamper.instance.updated_at.should be_nil
|
14
|
+
Article.new :updated_at => @today
|
15
|
+
Timestamper.instance.updated_at.should == @today
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#new_with_timestamper" do
|
20
|
+
it "should not set time if class is not reportable" do
|
21
|
+
class Draft < Content; end
|
22
|
+
|
23
|
+
Timestamper.instance.updated_at.should be_nil
|
24
|
+
Timestamper.configuration.exclude Draft
|
25
|
+
Draft.new :updated_at => @today
|
26
|
+
Timestamper.instance.updated_at.should be_nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "##timestamper_column" do
|
31
|
+
class MyArticle < Content; end
|
32
|
+
it "should set timestamper_column" do
|
33
|
+
MyArticle.class_eval do
|
34
|
+
timestamper_column :last_modified
|
35
|
+
end
|
36
|
+
Timestamper.instance.updated_at.should be_nil
|
37
|
+
MyArticle.new :last_modified => @today
|
38
|
+
Timestamper.instance.updated_at.should == @today
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Timestamper::Configuration do
|
4
|
+
before(:each) do
|
5
|
+
@config = Timestamper::Configuration.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#exclude" do
|
9
|
+
it "should exclude selected models" do
|
10
|
+
@config.exclude Article, Page
|
11
|
+
@config.excluded_models.should == [Article, Page]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#only" do
|
16
|
+
it "should only have selected models" do
|
17
|
+
@config.only Article
|
18
|
+
@config.only_models.should == [Article]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#reportable?" do
|
23
|
+
it "should query whether model is reportable?" do
|
24
|
+
@config.reportable?(Article).should be_true
|
25
|
+
|
26
|
+
@config.only Article
|
27
|
+
@config.reportable?(Article).should be_true
|
28
|
+
@config.reportable?(Page).should be_false
|
29
|
+
|
30
|
+
@config.exclude Article
|
31
|
+
@config.reportable?(Article).should be_false
|
32
|
+
@config.reportable?(Page).should be_false
|
33
|
+
|
34
|
+
@config = Timestamper::Configuration.new
|
35
|
+
@config.exclude Article
|
36
|
+
@config.reportable?(Article).should be_false
|
37
|
+
@config.reportable?(Page).should be_true
|
38
|
+
|
39
|
+
@config.only Article
|
40
|
+
@config.reportable?(Article).should be_false
|
41
|
+
@config.reportable?(Page).should be_false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#updated_at_column" do
|
46
|
+
it "should set updated_at_column" do
|
47
|
+
@config.updated_at_column = :updated_at
|
48
|
+
@config.updated_at_column.should == :updated_at
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rack/test'
|
3
|
+
require 'sinatra/base'
|
4
|
+
require 'timestamper/rack_middleware'
|
5
|
+
require 'active_resource/http_mock'
|
6
|
+
|
7
|
+
class MyApp < Sinatra::Base
|
8
|
+
use Timestamper::RackMiddleware
|
9
|
+
|
10
|
+
get "/" do
|
11
|
+
"homepage"
|
12
|
+
end
|
13
|
+
|
14
|
+
get "/articles" do
|
15
|
+
@article1 = Article.find 1
|
16
|
+
'articles'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe Timestamper::RackMiddleware do
|
21
|
+
include Rack::Test::Methods
|
22
|
+
before(:each) do
|
23
|
+
@today = Time.now
|
24
|
+
@yesterday = 1.day.ago
|
25
|
+
@tomorrow = 1.day.from_now
|
26
|
+
Timestamper.instance.reset
|
27
|
+
|
28
|
+
ActiveResource::HttpMock.respond_to do |mock|
|
29
|
+
mock.get "/articles/1.xml", {}, {:id => 1, :updated_at => @yesterday}.to_xml(:root => "article")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def app
|
34
|
+
my_app = MyApp.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def log_response_body(str)
|
38
|
+
File.open('response.html', 'w') do |f|
|
39
|
+
f.write str
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should have default Last-Modified http header" do
|
44
|
+
get "/"
|
45
|
+
last_response.body.should == "homepage"
|
46
|
+
last_response.headers["Last-Modified"].should_not be_nil
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should set Last-Modified time from article" do
|
50
|
+
get "/articles"
|
51
|
+
last_response.body.should == "articles"
|
52
|
+
last_response.headers["Last-Modified"].should == @yesterday.httpdate
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
describe Timestamper do
|
4
|
+
before do
|
5
|
+
@today = Time.now
|
6
|
+
@yesterday = 1.day.ago
|
7
|
+
@tomorrow = 1.day.from_now
|
8
|
+
Timestamper.instance.reset
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#instance" do
|
12
|
+
it "should implement singleton pattern" do
|
13
|
+
proc {Timestamper.new}.should raise_error
|
14
|
+
proc {Timestamper.instance}.should_not raise_error
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#reset" do
|
19
|
+
it "should reset itself" do
|
20
|
+
Timestamper.instance.updated_at = @today
|
21
|
+
Timestamper.instance.override = @yesterday
|
22
|
+
Timestamper.instance.reset
|
23
|
+
Timestamper.instance.updated_at.should be_nil
|
24
|
+
Timestamper.instance.override.should be_nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#update_at" do
|
29
|
+
it "should be able to set updated_at time" do
|
30
|
+
Timestamper.instance.updated_at.should be_nil
|
31
|
+
Timestamper.instance.updated_at = @today
|
32
|
+
Timestamper.instance.updated_at.should == @today
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should only change updated_at time if the new value is the latest time" do
|
36
|
+
Timestamper.instance.updated_at = @tomorrow
|
37
|
+
Timestamper.instance.updated_at = @today
|
38
|
+
Timestamper.instance.updated_at.should == @tomorrow
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#override" do
|
43
|
+
it "should override current updated_at value when updated_at is nil" do
|
44
|
+
Timestamper.instance.updated_at.should be_nil
|
45
|
+
Timestamper.instance.override = @today
|
46
|
+
Timestamper.instance.updated_at.should == @today
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should override current updated_at value" do
|
50
|
+
Timestamper.instance.updated_at = @tomorrow
|
51
|
+
Timestamper.instance.override = @today
|
52
|
+
Timestamper.instance.updated_at.should == @today
|
53
|
+
|
54
|
+
Timestamper.instance.updated_at = @today + 2.days
|
55
|
+
Timestamper.instance.updated_at.should == @today
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "##configuration" do
|
60
|
+
it "should return an instance of Timestamper::Configuration" do
|
61
|
+
Timestamper.configuration.class.should == Timestamper::Configuration
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
describe "##config" do
|
68
|
+
it "should accept a block" do
|
69
|
+
proc {Timestamper.config {}}.should_not raise_error
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should evaluate the block within the Timestamper.configuration instance" do
|
73
|
+
Timestamper.config do
|
74
|
+
self.should == Timestamper.configuration
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/timestamper.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{timestamper}
|
5
|
+
s.version = "0.0.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Dorren Chen"]
|
9
|
+
s.date = %q{2010-04-14}
|
10
|
+
s.description = %q{This gem sets Last-Modified in http response header with timestamp extracted from ActiveResource.}
|
11
|
+
s.email = %q{dorrenchen@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["README.rdoc", "lib/timestamper.rb", "lib/timestamper/activeresource.rb", "lib/timestamper/configuration.rb", "lib/timestamper/rack_middleware.rb", "lib/timestamper/timestamper.rb"]
|
13
|
+
s.files = ["README.rdoc", "Rakefile", "lib/timestamper.rb", "lib/timestamper/activeresource.rb", "lib/timestamper/configuration.rb", "lib/timestamper/rack_middleware.rb", "lib/timestamper/timestamper.rb", "spec/fixtures.rb", "spec/lib_specs/activeresource_spec.rb", "spec/lib_specs/configuration_spec.rb", "spec/lib_specs/rack_spec.rb", "spec/lib_specs/timestamper_spec.rb", "spec/spec_helper.rb", "Manifest", "timestamper.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/dorren/timestamper}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Timestamper", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{timestamper}
|
18
|
+
s.rubygems_version = %q{1.3.6}
|
19
|
+
s.summary = %q{This gem sets Last-Modified in http response header with timestamp extracted from ActiveResource.}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
26
|
+
else
|
27
|
+
end
|
28
|
+
else
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: timestamper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Dorren Chen
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-04-14 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: This gem sets Last-Modified in http response header with timestamp extracted from ActiveResource.
|
22
|
+
email: dorrenchen@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.rdoc
|
29
|
+
- lib/timestamper.rb
|
30
|
+
- lib/timestamper/activeresource.rb
|
31
|
+
- lib/timestamper/configuration.rb
|
32
|
+
- lib/timestamper/rack_middleware.rb
|
33
|
+
- lib/timestamper/timestamper.rb
|
34
|
+
files:
|
35
|
+
- README.rdoc
|
36
|
+
- Rakefile
|
37
|
+
- lib/timestamper.rb
|
38
|
+
- lib/timestamper/activeresource.rb
|
39
|
+
- lib/timestamper/configuration.rb
|
40
|
+
- lib/timestamper/rack_middleware.rb
|
41
|
+
- lib/timestamper/timestamper.rb
|
42
|
+
- spec/fixtures.rb
|
43
|
+
- spec/lib_specs/activeresource_spec.rb
|
44
|
+
- spec/lib_specs/configuration_spec.rb
|
45
|
+
- spec/lib_specs/rack_spec.rb
|
46
|
+
- spec/lib_specs/timestamper_spec.rb
|
47
|
+
- spec/spec_helper.rb
|
48
|
+
- Manifest
|
49
|
+
- timestamper.gemspec
|
50
|
+
has_rdoc: true
|
51
|
+
homepage: http://github.com/dorren/timestamper
|
52
|
+
licenses: []
|
53
|
+
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options:
|
56
|
+
- --line-numbers
|
57
|
+
- --inline-source
|
58
|
+
- --title
|
59
|
+
- Timestamper
|
60
|
+
- --main
|
61
|
+
- README.rdoc
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 1
|
77
|
+
- 2
|
78
|
+
version: "1.2"
|
79
|
+
requirements: []
|
80
|
+
|
81
|
+
rubyforge_project: timestamper
|
82
|
+
rubygems_version: 1.3.6
|
83
|
+
signing_key:
|
84
|
+
specification_version: 3
|
85
|
+
summary: This gem sets Last-Modified in http response header with timestamp extracted from ActiveResource.
|
86
|
+
test_files: []
|
87
|
+
|