rack-noncache 0.0.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.pryrc +6 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +15 -0
  7. data/README.md +36 -52
  8. data/Rakefile +16 -1
  9. data/features/cache_control.feature +22 -0
  10. data/features/step_definitions/steps.rb +46 -0
  11. data/features/support/continous_integration.rb +47 -0
  12. data/features/support/coverage.rb +18 -0
  13. data/features/support/env.rb +9 -0
  14. data/features/support/helpers.rb +13 -0
  15. data/features/support/web_app/application.rb +70 -0
  16. data/features/support/web_app/config_blacklist.ru +13 -0
  17. data/features/support/web_app/config_whitelist.ru +13 -0
  18. data/features/support/web_app/public/app/collections/todos.js +17 -0
  19. data/features/support/web_app/public/app/config.js +18 -0
  20. data/features/support/web_app/public/app/main.js +21 -0
  21. data/features/support/web_app/public/app/models/todo.js +33 -0
  22. data/features/support/web_app/public/app/templates/todo-add.html +3 -0
  23. data/features/support/web_app/public/app/templates/todo-editor.html +1 -0
  24. data/features/support/web_app/public/app/templates/todo-item.html +2 -0
  25. data/features/support/web_app/public/app/templates/todo-list-empty.html +1 -0
  26. data/features/support/web_app/public/app/templates/todo-stats.html +4 -0
  27. data/features/support/web_app/public/app/views/stats.js +26 -0
  28. data/features/support/web_app/public/app/views/todo.js +55 -0
  29. data/features/support/web_app/public/app/views/todoadd.js +48 -0
  30. data/features/support/web_app/public/app/views/todoedit.js +70 -0
  31. data/features/support/web_app/public/app/views/todolist.js +69 -0
  32. data/features/support/web_app/public/assets/css/bootstrap.css +3990 -0
  33. data/features/support/web_app/public/assets/css/style.css +82 -0
  34. data/features/support/web_app/public/assets/js/libs/backbone.js +1428 -0
  35. data/features/support/web_app/public/assets/js/libs/jquery-1.7.2.js +9404 -0
  36. data/features/support/web_app/public/assets/js/libs/require.js +2053 -0
  37. data/features/support/web_app/public/assets/js/libs/underscore.js +1008 -0
  38. data/features/support/web_app/public/assets/js/plugins/text.js +288 -0
  39. data/features/support/web_app/public/test/SpecRunner.html +42 -0
  40. data/features/support/web_app/public/test/lib/jasmine-1.2.0.rc3/MIT.LICENSE +20 -0
  41. data/features/support/web_app/public/test/lib/jasmine-1.2.0.rc3/jasmine-html.js +616 -0
  42. data/features/support/web_app/public/test/lib/jasmine-1.2.0.rc3/jasmine.css +81 -0
  43. data/features/support/web_app/public/test/lib/jasmine-1.2.0.rc3/jasmine.js +2530 -0
  44. data/features/support/web_app/public/test/lib/jasmine-jquery.js +306 -0
  45. data/features/support/web_app/public/test/spec/models/todo.coffee +37 -0
  46. data/features/support/web_app/public/test/spec/models/todo.js +48 -0
  47. data/features/support/web_app/public/test/spec/spec_helper.coffee +20 -0
  48. data/features/support/web_app/public/test/spec/spec_helper.js +17 -0
  49. data/features/support/web_app/public/test/spec/views/stats.coffee +9 -0
  50. data/features/support/web_app/public/test/spec/views/stats.js +13 -0
  51. data/features/support/web_app/public/test/spec/views/todo.coffee +22 -0
  52. data/features/support/web_app/public/test/spec/views/todo.js +26 -0
  53. data/features/support/web_app/public/test/spec/views/todoadd.coffee +22 -0
  54. data/features/support/web_app/public/test/spec/views/todoadd.js +29 -0
  55. data/features/support/web_app/public/test/spec/views/todoedit.coffee +59 -0
  56. data/features/support/web_app/public/test/spec/views/todoedit.js +72 -0
  57. data/features/support/web_app/public/test/spec/views/todolist.coffee +28 -0
  58. data/features/support/web_app/public/test/spec/views/todolist.js +39 -0
  59. data/features/support/web_app/views/details.erb +14 -0
  60. data/features/support/web_app/views/index.erb +43 -0
  61. data/lib/rack/noncache.rb +5 -7
  62. data/lib/rack/noncache/engine.rb +10 -26
  63. data/lib/rack/noncache/filters.rb +75 -0
  64. data/lib/rack/noncache/version.rb +1 -1
  65. data/rack-noncache.gemspec +32 -10
  66. metadata +382 -7
@@ -0,0 +1,22 @@
1
+ define ["views/todo"], (TodoView) ->
2
+ describe "TodoView", ->
3
+ beforeEach ->
4
+ @view = new TodoView(@viewOptions)
5
+ @view.render()
6
+
7
+ it "should render itself", ->
8
+ expect(@view.$el.text()).toMatch /new todo/
9
+
10
+ it "should remove itself when clicking on 'done'", ->
11
+ spyOn(@view.$el, "remove")
12
+ @view.$(".btn").click()
13
+ expect(@view.$el.remove).toHaveBeenCalled()
14
+
15
+ it "should update itself when the text of the model changes", ->
16
+ @view.model.setText "changed"
17
+ expect(@view.$el.text()).toMatch /changed/
18
+
19
+ it "should enter edit mode when dbl clicking the element", ->
20
+ spyOn(@view.editor, "attach")
21
+ @view.$el.click()
22
+ expect(@view.editor.attach).toHaveBeenCalled()
@@ -0,0 +1,26 @@
1
+
2
+ define(["views/todo"], function(TodoView) {
3
+ return describe("TodoView", function() {
4
+ beforeEach(function() {
5
+ this.view = new TodoView(this.viewOptions);
6
+ return this.view.render();
7
+ });
8
+ it("should render itself", function() {
9
+ return expect(this.view.$el.text()).toMatch(/new todo/);
10
+ });
11
+ it("should remove itself when clicking on 'done'", function() {
12
+ spyOn(this.view.$el, "remove");
13
+ this.view.$(".btn").click();
14
+ return expect(this.view.$el.remove).toHaveBeenCalled();
15
+ });
16
+ it("should update itself when the text of the model changes", function() {
17
+ this.view.model.setText("changed");
18
+ return expect(this.view.$el.text()).toMatch(/changed/);
19
+ });
20
+ return it("should enter edit mode when dbl clicking the element", function() {
21
+ spyOn(this.view.editor, "attach");
22
+ this.view.$el.click();
23
+ return expect(this.view.editor.attach).toHaveBeenCalled();
24
+ });
25
+ });
26
+ });
@@ -0,0 +1,22 @@
1
+ define ["views/todoadd"], (TodoAddView) ->
2
+ describe "TodoAddView", ->
3
+ beforeEach ->
4
+ @view = new TodoAddView(@viewOptions)
5
+ @view.render()
6
+
7
+ it "should create a new todo on add button click", ->
8
+ spyOn(@view.collection, "create")
9
+ @view.$(".btn").click()
10
+ expect(@view.collection.create).toHaveBeenCalled()
11
+
12
+ it "should create a new todo on pressing the enter key", ->
13
+ spyOn(@view.collection, "create")
14
+ keyPress = jQuery.Event("keypress", {keyCode:13})
15
+ @view.$("input").trigger(keyPress)
16
+ expect(@view.collection.create).toHaveBeenCalled()
17
+
18
+ it "should clear the input after creating a todo", ->
19
+ spyOn(@view, "getValue").andReturn("text")
20
+ spyOn(@view, "clearInput")
21
+ @view.add()
22
+ expect(@view.clearInput).toHaveBeenCalled()
@@ -0,0 +1,29 @@
1
+
2
+ define(["views/todoadd"], function(TodoAddView) {
3
+ return describe("TodoAddView", function() {
4
+ beforeEach(function() {
5
+ this.view = new TodoAddView(this.viewOptions);
6
+ return this.view.render();
7
+ });
8
+ it("should create a new todo on add button click", function() {
9
+ spyOn(this.view.collection, "create");
10
+ this.view.$(".btn").click();
11
+ return expect(this.view.collection.create).toHaveBeenCalled();
12
+ });
13
+ it("should create a new todo on pressing the enter key", function() {
14
+ var keyPress;
15
+ spyOn(this.view.collection, "create");
16
+ keyPress = jQuery.Event("keypress", {
17
+ keyCode: 13
18
+ });
19
+ this.view.$("input").trigger(keyPress);
20
+ return expect(this.view.collection.create).toHaveBeenCalled();
21
+ });
22
+ return it("should clear the input after creating a todo", function() {
23
+ spyOn(this.view, "getValue").andReturn("text");
24
+ spyOn(this.view, "clearInput");
25
+ this.view.add();
26
+ return expect(this.view.clearInput).toHaveBeenCalled();
27
+ });
28
+ });
29
+ });
@@ -0,0 +1,59 @@
1
+ define ["views/todoedit"], (TodoEditor) ->
2
+ describe "TodoEditor", ->
3
+ beforeEach ->
4
+ @view = new TodoEditor
5
+ @$element = $("<div><div id='todo-item'></div><div>")
6
+ setFixtures(@$element)
7
+
8
+ describe "attaching", ->
9
+ it "should hide the element", ->
10
+ spyOn(@$element, "hide")
11
+ @view.attach(@$element, @todo)
12
+ expect(@$element.hide).toHaveBeenCalled()
13
+
14
+ it "should set the value of the input to the models text", ->
15
+ @view.attach(@$element, @todo)
16
+ expect(@view.$input.val()).toMatch /new todo/
17
+
18
+ it "should focus the input", ->
19
+ @view.attach(@$element, @todo)
20
+ expect(@view.$input).toBeFocused()
21
+
22
+ describe "detaching", ->
23
+ beforeEach ->
24
+ @view.attach(@$element, @todo)
25
+
26
+ it "should show the hidden element again", ->
27
+ @view.detach()
28
+ expect(@$element).toBeVisible()
29
+
30
+ it "should detach the editor element", ->
31
+ @view.detach()
32
+ expect(@view.$el.parent().length).toBe(0)
33
+
34
+ describe "when attached", ->
35
+ beforeEach ->
36
+ @view.attach(@$element, @todo)
37
+ spyOn(@todo, "setText").andCallThrough()
38
+
39
+ it "should save and detach on blur", ->
40
+ @view.$input.blur()
41
+ expect(@todo.setText).toHaveBeenCalled()
42
+ expect(@view.attached).toBeFalsy()
43
+
44
+ it "should just detach on pressing ESC", ->
45
+ keyUp = jQuery.Event("keyup", {keyCode: 27})
46
+ @view.$input.trigger(keyUp)
47
+ expect(@todo.setText).not.toHaveBeenCalled()
48
+ expect(@view.attached).toBeFalsy()
49
+
50
+ it "should save and detach on pressing ENTER", ->
51
+ keyUp = jQuery.Event("keyup", {keyCode: 13})
52
+ @view.$input.trigger(keyUp)
53
+ expect(@todo.setText).toHaveBeenCalled()
54
+ expect(@view.attached).toBeFalsy()
55
+
56
+ it "should not detach when text is empty", ->
57
+ @view.$input.val("")
58
+ @view.save()
59
+ expect(@view.attached).toBeTruthy()
@@ -0,0 +1,72 @@
1
+
2
+ define(["views/todoedit"], function(TodoEditor) {
3
+ return describe("TodoEditor", function() {
4
+ beforeEach(function() {
5
+ this.view = new TodoEditor;
6
+ this.$element = $("<div><div id='todo-item'></div><div>");
7
+ return setFixtures(this.$element);
8
+ });
9
+ describe("attaching", function() {
10
+ it("should hide the element", function() {
11
+ spyOn(this.$element, "hide");
12
+ this.view.attach(this.$element, this.todo);
13
+ return expect(this.$element.hide).toHaveBeenCalled();
14
+ });
15
+ it("should set the value of the input to the models text", function() {
16
+ this.view.attach(this.$element, this.todo);
17
+ return expect(this.view.$input.val()).toMatch(/new todo/);
18
+ });
19
+ return it("should focus the input", function() {
20
+ this.view.attach(this.$element, this.todo);
21
+ return expect(this.view.$input).toBeFocused();
22
+ });
23
+ });
24
+ describe("detaching", function() {
25
+ beforeEach(function() {
26
+ return this.view.attach(this.$element, this.todo);
27
+ });
28
+ it("should show the hidden element again", function() {
29
+ this.view.detach();
30
+ return expect(this.$element).toBeVisible();
31
+ });
32
+ return it("should detach the editor element", function() {
33
+ this.view.detach();
34
+ return expect(this.view.$el.parent().length).toBe(0);
35
+ });
36
+ });
37
+ return describe("when attached", function() {
38
+ beforeEach(function() {
39
+ this.view.attach(this.$element, this.todo);
40
+ return spyOn(this.todo, "setText").andCallThrough();
41
+ });
42
+ it("should save and detach on blur", function() {
43
+ this.view.$input.blur();
44
+ expect(this.todo.setText).toHaveBeenCalled();
45
+ return expect(this.view.attached).toBeFalsy();
46
+ });
47
+ it("should just detach on pressing ESC", function() {
48
+ var keyUp;
49
+ keyUp = jQuery.Event("keyup", {
50
+ keyCode: 27
51
+ });
52
+ this.view.$input.trigger(keyUp);
53
+ expect(this.todo.setText).not.toHaveBeenCalled();
54
+ return expect(this.view.attached).toBeFalsy();
55
+ });
56
+ it("should save and detach on pressing ENTER", function() {
57
+ var keyUp;
58
+ keyUp = jQuery.Event("keyup", {
59
+ keyCode: 13
60
+ });
61
+ this.view.$input.trigger(keyUp);
62
+ expect(this.todo.setText).toHaveBeenCalled();
63
+ return expect(this.view.attached).toBeFalsy();
64
+ });
65
+ return it("should not detach when text is empty", function() {
66
+ this.view.$input.val("");
67
+ this.view.save();
68
+ return expect(this.view.attached).toBeTruthy();
69
+ });
70
+ });
71
+ });
72
+ });
@@ -0,0 +1,28 @@
1
+ define ["views/todolist", "models/todo"], (TodoListView, Todo) ->
2
+ describe "TodoListView", ->
3
+ beforeEach ->
4
+ @view = new TodoListView(@viewOptions)
5
+ $element = sandbox(id: "todo-list")
6
+ $element.append(@view.render().el)
7
+
8
+ it "should append a new item on the 'add' event", ->
9
+ @view.collection.trigger("add", new Todo)
10
+ expect(@view.$el.children().length).toEqual 1
11
+
12
+ # test if new item has fade-in state
13
+ expect(@view.$el.children()[0]).toHaveClass("new transition-bg")
14
+
15
+ it "should append all undone items on the 'reset' event", ->
16
+ @view.collection.add([new Todo, new Todo, new Todo(done:true)], silent: true)
17
+ @view.collection.trigger("reset", @view.collection)
18
+ expect(@view.$el.children().length).toEqual 2
19
+
20
+ it "should show a message instead of the list when no more items are remaining", ->
21
+ @view.collection.reset()
22
+ expect(@view.$el.parent().length).toEqual 0
23
+ expect(@view.$emptyList.parent().length).toEqual 1
24
+
25
+ it "should hide the empty message before a new item is added", ->
26
+ @view.collection.reset()
27
+ @view.collection.add(@todo)
28
+ expect(@view.$emptyList.parent().length).toEqual 0
@@ -0,0 +1,39 @@
1
+
2
+ define(["views/todolist", "models/todo"], function(TodoListView, Todo) {
3
+ return describe("TodoListView", function() {
4
+ beforeEach(function() {
5
+ var $element;
6
+ this.view = new TodoListView(this.viewOptions);
7
+ $element = sandbox({
8
+ id: "todo-list"
9
+ });
10
+ return $element.append(this.view.render().el);
11
+ });
12
+ it("should append a new item on the 'add' event", function() {
13
+ this.view.collection.trigger("add", new Todo);
14
+ expect(this.view.$el.children().length).toEqual(1);
15
+ return expect(this.view.$el.children()[0]).toHaveClass("new transition-bg");
16
+ });
17
+ it("should append all undone items on the 'reset' event", function() {
18
+ this.view.collection.add([
19
+ new Todo, new Todo, new Todo({
20
+ done: true
21
+ })
22
+ ], {
23
+ silent: true
24
+ });
25
+ this.view.collection.trigger("reset", this.view.collection);
26
+ return expect(this.view.$el.children().length).toEqual(2);
27
+ });
28
+ it("should show a message instead of the list when no more items are remaining", function() {
29
+ this.view.collection.reset();
30
+ expect(this.view.$el.parent().length).toEqual(0);
31
+ return expect(this.view.$emptyList.parent().length).toEqual(1);
32
+ });
33
+ return it("should hide the empty message before a new item is added", function() {
34
+ this.view.collection.reset();
35
+ this.view.collection.add(this.todo);
36
+ return expect(this.view.$emptyList.parent().length).toEqual(0);
37
+ });
38
+ });
39
+ });
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Todos Details</title>
6
+ </head>
7
+ <body>
8
+ <div class="shell container-fluid">
9
+ <div class="page-header">
10
+ <h1>Todos</h1>
11
+ </div>
12
+ </div>
13
+ </body>
14
+ </html>
@@ -0,0 +1,43 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Todos</title>
6
+ <link href="assets/css/bootstrap.css" rel="stylesheet">
7
+ <link href="assets/css/style.css" rel="stylesheet">
8
+ <script data-main="app/config" src="assets/js/libs/require.js"></script>
9
+ <script>
10
+ // define the bootstrap module inside index.erb so we can easily inject the default collection
11
+ define("bootstrap", [], function() {
12
+ return function(todoList) {
13
+ todoList.reset(<%= @todos.to_json %>);
14
+ };
15
+ });
16
+
17
+ // require main to kickstart the app
18
+ require(["main"]);
19
+ </script>
20
+ </head>
21
+ <body>
22
+ <div class="shell container-fluid">
23
+ <div class="page-header">
24
+ <h1>Todos</h1>
25
+ </div>
26
+ <div id="todo-container" class="hidden transition-opacity row-fluid">
27
+ <div class="span2">
28
+ <div id="todo-stats"></div>
29
+ </div>
30
+
31
+ <div class="span10">
32
+ <div class="row">
33
+ <div class="span8">
34
+ <div id="todo-add"></div>
35
+ <div id="todo-list"></div>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <a href='/details'>Show details</a>
42
+ </body>
43
+ </html>
data/lib/rack/noncache.rb CHANGED
@@ -1,13 +1,11 @@
1
- require "rack"
2
- require "rack/noncache/version"
3
- require "rack/noncache/engine"
1
+ require 'rack'
2
+ require 'rack/noncache/version'
3
+ require 'rack/noncache/engine'
4
4
 
5
5
  module Rack
6
6
  module NonCache
7
-
8
- def self.new(backend, options={}, &b)
9
- Engine.new(backend, options, &b)
7
+ def self.new(backend, opts = {}, &b)
8
+ Engine.new(backend, opts, &b)
10
9
  end
11
-
12
10
  end
13
11
  end
@@ -1,6 +1,7 @@
1
+ require_relative 'filters'
2
+
1
3
  module Rack
2
4
  module NonCache
3
-
4
5
  class Engine
5
6
  attr_accessor :whitelist, :blacklist
6
7
 
@@ -15,33 +16,18 @@ module Rack
15
16
  # execute the request using our backend
16
17
  status, headers, response = @backend.call(env)
17
18
 
18
- if target_path? env['REQUEST_URI']
19
-
20
- headers['HTTP_CACHE_CONTROL'] =
21
- 'no-cache, max-age=0, must-revalidate, no-store, private, '
22
-
23
- # Set standard HTTP/1.0 no-cache header.
24
- headers['HTTP_CACHE_CONTROL'] +=
25
- 'max-stale=0, post-check=0, pre-check=0, ' +
26
- 'no-cache=\"Set-Cookie, Set-Cookie2\"'
27
-
28
- # Set standard HTTP/1.1 no-cache header.
29
- headers['HTTP_PRAGMA'] = 'no-cache'
30
-
31
- # Set to expire far in the past. Prevents caching at the proxy server
32
- headers['HTTP_EXPIRES'] = Time.now.httpdate
33
-
34
- # Deprecated header
35
- headers['HTTP_KEEP_ALIVE'] = 'timeout=3, max=993'
36
-
37
- headers['X-Content-Type-Options'] = 'nosniff'
38
- end
39
-
19
+ uri = env['REQUEST_URI']
20
+ filters.each { |filter| filter.apply(headers) } if target_path? uri
40
21
  [status, headers, response]
41
22
  end
42
23
 
43
24
  private
44
25
 
26
+ def filters
27
+ [Http10Filter, Http11Filter, ProxyFilter, SecurityFilter,
28
+ DeprecatedFilter]
29
+ end
30
+
45
31
  def target_path?(uri)
46
32
  if @whitelist
47
33
  return match(@whitelist, uri)
@@ -55,11 +41,9 @@ module Rack
55
41
  def match(list, uri)
56
42
  list.index do |path|
57
43
  (path.class.eql?(String) && path.eql?(uri)) ||
58
- (path.class.eql?(Regexp) && path.match(uri))
44
+ (path.class.eql?(Regexp) && path.match(uri))
59
45
  end
60
46
  end
61
-
62
47
  end
63
-
64
48
  end
65
49
  end