timestamper 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,6 @@
1
+ require 'singleton'
2
+ require 'activeresource'
3
+ require 'timestamper/timestamper'
4
+ require 'timestamper/activeresource'
5
+ require 'timestamper/configuration'
6
+ require 'timestamper/rack_middleware'
@@ -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
@@ -0,0 +1,8 @@
1
+
2
+ # testing fixtures
3
+ class Content < ActiveResource::Base
4
+ self.site = ""
5
+ end
6
+ class Article < Content; end
7
+ class Page < Content; end
8
+ class Comment < Content; end
@@ -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
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH << './lib'
2
+ require 'timestamper'
3
+ require 'fixtures'
@@ -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
+