rack-noncache 0.0.4 → 1.0.0

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.
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