omnivorous_etag 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.extra_files << "../some/external/dependency.rb"
7
+ #
8
+ # at.libs << ":../some/external"
9
+ #
10
+ # at.add_exception 'vendor'
11
+ #
12
+ # at.add_mapping(/dependency.rb/) do |f, _|
13
+ # at.files_matching(/test_.*rb$/)
14
+ # end
15
+ #
16
+ # %w(TestA TestB).each do |klass|
17
+ # at.extra_class_map[klass] = "test/test_misc.rb"
18
+ # end
19
+ # end
20
+
21
+ # Autotest.add_hook :run_command do |at|
22
+ # system "rake build"
23
+ # end
File without changes
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2011-11-01
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,8 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/omnivorous_etag
7
+ lib/omnivorous_etag.rb
8
+ test/test_omnivorous_etag.rb
@@ -0,0 +1,56 @@
1
+ = omnivorous_etag
2
+
3
+ http://github.com/julik/omnivorous_etag
4
+
5
+ == DESCRIPTION:
6
+
7
+ ETags are good, however normally they are generated based on strings. However, very often it is easier
8
+ to pass in a complete model object as your ETag, or it's parametrized represenation (record id) together
9
+ with the version. Or an array of objects (if you want to cache your object listing page and prevent it
10
+ from spending time on template rendering).
11
+
12
+ This module will take care of transforming any object into a stringified representation that is usable as an etag
13
+ with minimum fuss.
14
+
15
+ == SYNOPSIS:
16
+
17
+ In the body of your Sinatra application or any other web application responder class that
18
+ has to send ETags
19
+
20
+ include OmnivorousEtag
21
+
22
+ If the superclass of your request handler has an etag method this method will be called with the stringified
23
+ result of the omnivirous etag generation (so that for example the request can be 304-terminated).
24
+
25
+ == REQUIREMENTS:
26
+
27
+ * FIX (list of requirements)
28
+
29
+ == INSTALL:
30
+
31
+ * FIX (sudo gem install, anything else)
32
+
33
+ == LICENSE:
34
+
35
+ (The MIT License)
36
+
37
+ Copyright (c) 2011 me@julik.nl
38
+
39
+ Permission is hereby granted, free of charge, to any person obtaining
40
+ a copy of this software and associated documentation files (the
41
+ 'Software'), to deal in the Software without restriction, including
42
+ without limitation the rights to use, copy, modify, merge, publish,
43
+ distribute, sublicense, and/or sell copies of the Software, and to
44
+ permit persons to whom the Software is furnished to do so, subject to
45
+ the following conditions:
46
+
47
+ The above copyright notice and this permission notice shall be
48
+ included in all copies or substantial portions of the Software.
49
+
50
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
51
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
52
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
53
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
54
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
55
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
56
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,10 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.spec 'omnivorous_etag' do
7
+ developer('Julik Tarkhanov', 'me@julik.nl')
8
+ end
9
+
10
+ # vim: syntax=ruby
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ abort "you need to write me"
@@ -0,0 +1,54 @@
1
+ require "base64"
2
+ require "zlib"
3
+
4
+ module OmnivorousEtag
5
+ VERSION = '1.0.0'
6
+
7
+ class EtagCannotBeNil < TypeError
8
+ end
9
+
10
+ # The etag method that takes anything, with the following behavior quirks
11
+ def etag(anything)
12
+ raise EtagCannotBeNil, "Cannot generate ETags for nils" if anything.nil?
13
+
14
+ data = if anything.respond_to?(:map)
15
+ anything.map{|e| etag(e) }.join(";")
16
+ elsif anything.respond_to?(:to_param) && OmnivorousEtag.is_versioned?(anything)
17
+ etag([anything.class.to_s, anything.to_param, OmnivorousEtag.extract_version(anything)])
18
+ elsif anything.is_a?(String) || anything.is_a?(Numeric) || anything.is_a?(Symbol)
19
+ anything.to_s
20
+ else
21
+ Marshal.dump(anything)
22
+ end
23
+
24
+ if defined?(super)
25
+ super(OmnivorousEtag.package(data))
26
+ else
27
+ OmnivorousEtag.package(data)
28
+ end
29
+ end
30
+
31
+ def self.package(blob)
32
+ Base64.encode64(blob)
33
+ end
34
+
35
+ def self.extract_version(object)
36
+ if object.respond_to?(:new_record) && object.new_record?
37
+ "new"
38
+ # http://rubydoc.info/gems/acts_as_revisable
39
+ elsif object.respond_to?(:revision_number)
40
+ object.revision_number
41
+ # http://rubydoc.info/gems/vestal_versions
42
+ # http://rubydoc.info/gems/acts_as_versioned
43
+ elsif object.respond_to?(:version)
44
+ object.version
45
+ end
46
+ nil
47
+ end
48
+
49
+ # Tells whether the passed AR record object is using a versioning plugin (in which case we
50
+ # can serve ourselves with the version tag it carries)
51
+ def self.is_versioned?(object)
52
+ extract_version(object) != nil
53
+ end
54
+ end
@@ -0,0 +1,104 @@
1
+ require "test/unit"
2
+ require "omnivorous_etag"
3
+
4
+ class TestOmnivorousEtag < Test::Unit::TestCase
5
+ class Bearer
6
+ def etag(string)
7
+ raise "Not a string" unless string.is_a?(String)
8
+ return string
9
+ end
10
+ end
11
+
12
+ class RaisingBearer
13
+ def etag(something)
14
+ throw(:caught)
15
+ end
16
+ end
17
+
18
+ class App < Bearer
19
+ include OmnivorousEtag
20
+ end
21
+
22
+ class RaisingApp < RaisingBearer
23
+ end
24
+
25
+ def test_with_string
26
+ app = App.new
27
+ assert_equal "Mw==\n", app.etag(3)
28
+ end
29
+
30
+ def test_work_on_blank_object_should_just_return
31
+ o = Object.new
32
+ class << o; include OmnivorousEtag; end
33
+ assert_equal "Mw==\n", o.etag(3)
34
+ end
35
+
36
+ def test_work_on_object_that_supports_super_should_callout_to_super
37
+ assert_throws(:caught) { RaisingApp.new.etag(3) }
38
+ end
39
+
40
+ def test_work_on_blank_object_should_just_return
41
+ o = Object.new
42
+ class << o; include OmnivorousEtag; end
43
+ assert_equal "Mw==\n", o.etag(3)
44
+ end
45
+
46
+ def test_with_float
47
+ app = App.new
48
+ assert_equal "My4zNQ==\n", app.etag(3.35)
49
+ end
50
+
51
+ def test_with_int
52
+ assert_equal "MQ==\n", App.new.etag(1)
53
+ end
54
+
55
+ def test_with_symbol
56
+ assert_equal "YQ==\n", App.new.etag(:a)
57
+ end
58
+
59
+ def test_with_string
60
+ assert_equal "RXhwZXJpbWVudGFs\n", App.new.etag("Experimental")
61
+ end
62
+
63
+ def test_with_array
64
+ assert_equal "TVE9PQo7TWc9PQo7TXc9PQo=\n", App.new.etag([1,2,3])
65
+ end
66
+
67
+ def test_with_array
68
+ assert_equal "TVE9PQo7TWc9PQo7TXc9PQo=\n", App.new.etag([1,2,3])
69
+ end
70
+
71
+ def test_with_version_activerecord_using_acts_as_versioned
72
+ r = Struct.new(:version, :to_param).new(1, "1-my-blog_entry")
73
+ r_newer = Struct.new(:version, :to_param).new(2, "1-my-blog_entry")
74
+ assert_equal "TVE9PQo7TVMxdGVTMWliRzluWDJWdWRISjUK\n", App.new.etag(r)
75
+ assert_equal "TWc9PQo7TVMxdGVTMWliRzluWDJWdWRISjUK\n", App.new.etag(r_newer)
76
+ end
77
+
78
+ def test_raises_with_nil
79
+ assert_raise(OmnivorousEtag::EtagCannotBeNil) do
80
+ App.new.etag(nil)
81
+ end
82
+ end
83
+
84
+ def test_with_version_activerecord_using_revisions
85
+ r = Struct.new(:revision_number, :to_param).new(1, "1-my-blog_entry")
86
+ r_newer = Struct.new(:revision_number, :to_param).new(2, "1-my-blog_entry")
87
+ assert_equal "TVE9PQo7TVMxdGVTMWliRzluWDJWdWRISjUK\n", App.new.etag(r)
88
+ assert_equal "TWc9PQo7TVMxdGVTMWliRzluWDJWdWRISjUK\n", App.new.etag(r_newer)
89
+ end
90
+
91
+
92
+ class Arbitrary < Struct.new(:a,:foo,:bar)
93
+ end
94
+
95
+ class Another < Arbitrary
96
+ end
97
+
98
+ def test_arbitraty_object_carries_class_name
99
+ a = Arbitrary.new("foo", "bar", "baz")
100
+ another = Another.new("foo", "bar", "baz")
101
+ assert_not_equal App.new.etag(a), App.new.etag(another),
102
+ "Objects of different classes should produce different etags"
103
+ end
104
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omnivorous_etag
3
+ version: !ruby/object:Gem::Version
4
+ version: !binary |-
5
+ MS4wLjA=
6
+ prerelease:
7
+ platform: ruby
8
+ authors:
9
+ - Julik Tarkhanov
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-11-01 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ requirement: &3947390 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '2.12'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *3947390
26
+ description: !binary |-
27
+ RVRhZ3MgYXJlIGdvb2QsIGhvd2V2ZXIgbm9ybWFsbHkgdGhleSBhcmUgZ2Vu
28
+ ZXJhdGVkIGJhc2VkIG9uIHN0cmluZ3MuIEhvd2V2ZXIsIHZlcnkgb2Z0ZW4g
29
+ aXQgaXMgZWFzaWVyCnRvIHBhc3MgaW4gYSBjb21wbGV0ZSBtb2RlbCBvYmpl
30
+ Y3QgYXMgeW91ciBFVGFnLCBvciBpdCdzIHBhcmFtZXRyaXplZCByZXByZXNl
31
+ bmF0aW9uIChyZWNvcmQgaWQpIHRvZ2V0aGVyCndpdGggdGhlIHZlcnNpb24u
32
+ IE9yIGFuIGFycmF5IG9mIG9iamVjdHMgKGlmIHlvdSB3YW50IHRvIGNhY2hl
33
+ IHlvdXIgb2JqZWN0IGxpc3RpbmcgcGFnZSBhbmQgcHJldmVudCBpdApmcm9t
34
+ IHNwZW5kaW5nIHRpbWUgb24gdGVtcGxhdGUgcmVuZGVyaW5nKS4KClRoaXMg
35
+ bW9kdWxlIHdpbGwgdGFrZSBjYXJlIG9mIHRyYW5zZm9ybWluZyBhbnkgb2Jq
36
+ ZWN0IGludG8gYSBzdHJpbmdpZmllZCByZXByZXNlbnRhdGlvbiB0aGF0IGlz
37
+ IHVzYWJsZSBhcyBhbiBldGFnCndpdGggbWluaW11bSBmdXNzLg==
38
+ email:
39
+ - me@julik.nl
40
+ executables:
41
+ - !binary |-
42
+ b21uaXZvcm91c19ldGFn
43
+ extensions: []
44
+ extra_rdoc_files:
45
+ - !binary |-
46
+ SGlzdG9yeS50eHQ=
47
+ - !binary |-
48
+ TWFuaWZlc3QudHh0
49
+ - !binary |-
50
+ UkVBRE1FLnR4dA==
51
+ files:
52
+ - !binary |-
53
+ LmF1dG90ZXN0
54
+ - !binary |-
55
+ SGlzdG9yeS50eHQ=
56
+ - !binary |-
57
+ TWFuaWZlc3QudHh0
58
+ - !binary |-
59
+ UkVBRE1FLnR4dA==
60
+ - !binary |-
61
+ UmFrZWZpbGU=
62
+ - !binary |-
63
+ YmluL29tbml2b3JvdXNfZXRhZw==
64
+ - !binary |-
65
+ bGliL29tbml2b3JvdXNfZXRhZy5yYg==
66
+ - !binary |-
67
+ dGVzdC90ZXN0X29tbml2b3JvdXNfZXRhZy5yYg==
68
+ - .gemtest
69
+ homepage: !binary |-
70
+ aHR0cDovL2dpdGh1Yi5jb20vanVsaWsvb21uaXZvcm91c19ldGFn
71
+ licenses: []
72
+ post_install_message:
73
+ rdoc_options:
74
+ - --main
75
+ - README.txt
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project: omnivorous_etag
92
+ rubygems_version: 1.8.11
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: !binary |-
96
+ RVRhZ3MgYXJlIGdvb2QsIGhvd2V2ZXIgbm9ybWFsbHkgdGhleSBhcmUgZ2Vu
97
+ ZXJhdGVkIGJhc2VkIG9uIHN0cmluZ3M=
98
+ test_files:
99
+ - test/test_omnivorous_etag.rb