keynote 1.1.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +73 -25
  3. data/{LICENSE → LICENSE.txt} +2 -6
  4. data/README.md +119 -47
  5. data/lib/generators/presenter_generator.rb +16 -24
  6. data/lib/keynote/cache.rb +1 -1
  7. data/lib/keynote/controller.rb +4 -5
  8. data/lib/keynote/core.rb +64 -0
  9. data/lib/keynote/helper.rb +4 -5
  10. data/lib/keynote/inline.rb +51 -109
  11. data/lib/keynote/presenter.rb +13 -19
  12. data/lib/keynote/railtie.rb +7 -24
  13. data/lib/keynote/rumble.rb +22 -13
  14. data/lib/keynote/testing/{minitest_rails.rb → minitest.rb} +1 -2
  15. data/lib/keynote/testing/rspec.rb +5 -5
  16. data/lib/keynote/testing/test_present_method.rb +2 -2
  17. data/lib/keynote/testing/test_unit.rb +1 -1
  18. data/lib/keynote/version.rb +2 -2
  19. data/lib/keynote.rb +7 -65
  20. metadata +68 -96
  21. data/.editorconfig +0 -14
  22. data/.gitignore +0 -18
  23. data/.travis.yml +0 -11
  24. data/.yardopts +0 -4
  25. data/Gemfile +0 -4
  26. data/Rakefile +0 -21
  27. data/keynote.gemspec +0 -37
  28. data/lib/generators/templates/keynote_mini_test_unit.rb +0 -11
  29. data/lib/keynote/testing/minitest_rails.rake +0 -7
  30. data/scenarios/rails31.docker-compose.yml +0 -15
  31. data/scenarios/rails31.dockerfile +0 -5
  32. data/scenarios/rails31.gemfile +0 -7
  33. data/scenarios/rails32.docker-compose.yml +0 -15
  34. data/scenarios/rails32.dockerfile +0 -5
  35. data/scenarios/rails32.gemfile +0 -6
  36. data/scenarios/rails40.docker-compose.yml +0 -15
  37. data/scenarios/rails40.dockerfile +0 -5
  38. data/scenarios/rails40.gemfile +0 -6
  39. data/scenarios/rails41.docker-compose.yml +0 -15
  40. data/scenarios/rails41.dockerfile +0 -5
  41. data/scenarios/rails41.gemfile +0 -6
  42. data/scenarios/rails42.docker-compose.yml +0 -15
  43. data/scenarios/rails42.dockerfile +0 -5
  44. data/scenarios/rails42.gemfile +0 -6
  45. data/scenarios/rails50.docker-compose.yml +0 -15
  46. data/scenarios/rails50.dockerfile +0 -5
  47. data/scenarios/rails50.gemfile +0 -6
  48. data/scenarios/rails51.docker-compose.yml +0 -15
  49. data/scenarios/rails51.dockerfile +0 -5
  50. data/scenarios/rails51.gemfile +0 -6
  51. data/scenarios.yml +0 -25
  52. data/spec/benchmarks.rb +0 -73
  53. data/spec/generator_spec.rb +0 -159
  54. data/spec/helper.rb +0 -90
  55. data/spec/inline_spec.rb +0 -157
  56. data/spec/keynote_spec.rb +0 -130
  57. data/spec/presenter_spec.rb +0 -179
  58. data/spec/railtie_spec.rb +0 -33
  59. data/spec/rumble_spec.rb +0 -271
  60. data/spec/test_case_spec.rb +0 -12
  61. /data/lib/generators/templates/{keynote_mini_test_spec.rb → keynote_mini_test_spec.rb.tt} +0 -0
  62. /data/lib/generators/templates/{keynote_test_unit.rb → keynote_mini_test_unit.rb.tt} +0 -0
  63. /data/lib/generators/templates/{keynote_presenter.rb → keynote_presenter.rb.tt} +0 -0
  64. /data/lib/generators/templates/{keynote_rspec.rb → keynote_rspec.rb.tt} +0 -0
data/scenarios.yml DELETED
@@ -1,25 +0,0 @@
1
- project: keynote
2
-
3
- shared:
4
- from: ruby:2.4
5
- cmd: "(bundle check || bundle install) && bundle exec rake"
6
- service:
7
- volumes:
8
- - bundle_{{scenario_name}}:/usr/local/bundle
9
- environment:
10
- BUNDLE_GEMFILE: scenarios/{{scenario_name}}.gemfile
11
- volumes:
12
- bundle_{{scenario_name}}:
13
-
14
- scenarios:
15
- rails31:
16
- from: ruby:2.0
17
- rails32:
18
- from: ruby:2.0
19
- rails40:
20
- from: ruby:2.3
21
- rails41:
22
- from: ruby:2.3
23
- rails42: {}
24
- rails50: {}
25
- rails51: {}
data/spec/benchmarks.rb DELETED
@@ -1,73 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require "rails"
4
- require "action_controller/railtie"
5
- require "action_mailer/railtie"
6
- require "rails/test_unit/railtie"
7
- require "keynote"
8
- require "benchmark"
9
-
10
- class MyPresenter < Keynote::Presenter
11
- extend Keynote::Inline
12
- inline :erb
13
-
14
- def my_string
15
- "a" + "b" + "c"
16
- end
17
-
18
- def rumble
19
- a_local = 1000
20
-
21
- build_html do
22
- div.foobar.baz! do
23
- p { my_string }
24
- p { a_local }
25
- end
26
- end
27
- end
28
-
29
- def erb_hash
30
- a_local = 1000
31
-
32
- erb a_local: a_local
33
- # <div class="foobar" id="baz">
34
- # <p><%= my_string %></p>
35
- # <p><%= a_local %></p>
36
- # </div>
37
- end
38
-
39
- def erb_binding
40
- a_local = 1000
41
-
42
- erb binding
43
- # <div class="foobar" id="baz">
44
- # <p><%= my_string %></p>
45
- # <p><%= a_local %></p>
46
- # </div>
47
- end
48
-
49
- def raw_erb_template
50
- source = %{
51
- <div class="foobar" id="baz">
52
- <p><%= my_string %></p>
53
- <p><%= a_local %></p>
54
- </div>
55
- }
56
- template = ActionView::Template.new(
57
- source, "raw_erb_template",
58
- ActionView::Template.handler_for_extension(:erb),
59
- locals: [:a_local]
60
- )
61
- TESTS.times { template.render(self, a_local: 1000) }
62
- end
63
- end
64
-
65
- TESTS = 1_000
66
- presenter = MyPresenter.new(:view)
67
-
68
- Benchmark.bmbm do |results|
69
- results.report("rumble") { TESTS.times { presenter.rumble } }
70
- results.report("erb_hash") { TESTS.times { presenter.erb_hash } }
71
- results.report("erb_binding") { TESTS.times { presenter.erb_binding } }
72
- results.report("raw_erb_template") { presenter.raw_erb_template }
73
- end
@@ -1,159 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require 'helper'
4
- require 'rails/generators'
5
- require 'fileutils'
6
-
7
- describe "generators" do
8
- def invoke_generator(*args)
9
- FileUtils.mkdir_p(output_path)
10
-
11
- args.push '--quiet'
12
-
13
- retval = Rails::Generators.invoke 'presenter', args,
14
- :behavior => :invoke,
15
- :destination_root => output_path
16
-
17
- assert retval, 'Generator must succeed'
18
-
19
- output_files = Dir["#{output_path}/**/*.rb"]
20
-
21
- yield output_files.map { |path| path.sub("#{output_path}/", '') }.sort
22
- ensure
23
- FileUtils.rm_rf(output_path)
24
- end
25
-
26
- def output_path
27
- File.expand_path('../../tmp', __FILE__)
28
- end
29
-
30
- def file_contents(path)
31
- File.read(File.join(output_path, path))
32
- end
33
-
34
- describe "when the test_framework is :test_unit" do
35
- before do
36
- Rails.application.config.generators do |g|
37
- g.test_framework :test_unit
38
- end
39
- end
40
-
41
- it "generates a presenter and Test::Unit file" do
42
- invoke_generator 'post' do |files|
43
- files.must_equal %w(
44
- app/presenters/post_presenter.rb
45
- test/unit/presenters/post_presenter_test.rb
46
- )
47
-
48
- file_contents('app/presenters/post_presenter.rb').
49
- must_match(/class PostPresenter < Keynote::Presenter/)
50
-
51
- file_contents('test/unit/presenters/post_presenter_test.rb').
52
- must_match(/class PostPresenterTest < Keynote::TestCase/)
53
- end
54
- end
55
-
56
- it "does not add a 'presents' line" do
57
- invoke_generator 'post' do |files|
58
- file_contents('app/presenters/post_presenter.rb').
59
- wont_match(/presents/)
60
- end
61
- end
62
-
63
- it "generates an appropriate present() call" do
64
- invoke_generator 'post' do |files|
65
- file_contents('test/unit/presenters/post_presenter_test.rb').
66
- must_match(/present\(:post\)/)
67
- end
68
- end
69
-
70
- describe "when the presenter has one parameter" do
71
- it "adds a 'presents' line" do
72
- invoke_generator 'post', 'foo' do |files|
73
- file_contents('app/presenters/post_presenter.rb').
74
- must_match(/presents :foo$/)
75
- end
76
- end
77
-
78
- it "generates an appropriate present() call" do
79
- invoke_generator 'post', 'foo' do |files|
80
- file_contents('test/unit/presenters/post_presenter_test.rb').
81
- must_match(/present\(:post, :foo\)/)
82
- end
83
- end
84
- end
85
-
86
- describe "when the presenter has two parameters" do
87
- it "adds a 'presents' line" do
88
- invoke_generator 'post', 'foo', 'bar' do |files|
89
- file_contents('app/presenters/post_presenter.rb').
90
- must_match(/presents :foo, :bar$/)
91
- end
92
- end
93
-
94
- it "generates an appropriate present() call" do
95
- invoke_generator 'post', 'foo', 'bar' do |files|
96
- file_contents('test/unit/presenters/post_presenter_test.rb').
97
- must_match(/present\(:post, :foo, :bar\)/)
98
- end
99
- end
100
- end
101
- end
102
-
103
- it "generates a presenter and RSpec file" do
104
- Rails.application.config.generators do |g|
105
- g.test_framework :rspec
106
- end
107
-
108
- invoke_generator 'post' do |files|
109
- files.must_equal %w(
110
- app/presenters/post_presenter.rb
111
- spec/presenters/post_presenter_spec.rb
112
- )
113
-
114
- file_contents('app/presenters/post_presenter.rb').
115
- must_match(/class PostPresenter < Keynote::Presenter/)
116
-
117
- file_contents('spec/presenters/post_presenter_spec.rb').
118
- must_match(/describe PostPresenter do/)
119
- end
120
- end
121
-
122
- it "generates a presenter and MiniTest::Rails spec file" do
123
- Rails.application.config.generators do |g|
124
- g.test_framework :mini_test, :spec => true
125
- end
126
-
127
- invoke_generator 'post' do |files|
128
- files.must_equal %w(
129
- app/presenters/post_presenter.rb
130
- test/presenters/post_presenter_test.rb
131
- )
132
-
133
- file_contents('app/presenters/post_presenter.rb').
134
- must_match(/class PostPresenter < Keynote::Presenter/)
135
-
136
- file_contents('test/presenters/post_presenter_test.rb').
137
- must_match(/describe PostPresenter do/)
138
- end
139
- end
140
-
141
- it "generates a presenter and MiniTest::Rails unit file" do
142
- Rails.application.config.generators do |g|
143
- g.test_framework :mini_test, :spec => false
144
- end
145
-
146
- invoke_generator 'post' do |files|
147
- files.must_equal %w(
148
- app/presenters/post_presenter.rb
149
- test/presenters/post_presenter_test.rb
150
- )
151
-
152
- file_contents('app/presenters/post_presenter.rb').
153
- must_match(/class PostPresenter < Keynote::Presenter/)
154
-
155
- file_contents('test/presenters/post_presenter_test.rb').
156
- must_match(/class PostPresenterTest < Keynote::TestCase/)
157
- end
158
- end
159
- end
data/spec/helper.rb DELETED
@@ -1,90 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- if ENV["COVERAGE"]
4
- require "simplecov"
5
- SimpleCov.start
6
- end
7
-
8
- require 'minitest/autorun'
9
- require 'minitest/spec'
10
- require 'minitest/pride'
11
- require 'mocha/setup'
12
-
13
- require 'pry'
14
-
15
- require 'rails'
16
- require 'action_controller/railtie'
17
- require 'action_mailer/railtie'
18
- require 'rails/test_unit/railtie'
19
-
20
- require 'keynote'
21
-
22
- ## Initialize our test app (by Jose Valim: https://gist.github.com/1942658)
23
-
24
- class TestApp < Rails::Application
25
- config.active_support.deprecation = :log
26
- config.eager_load = false
27
-
28
- config.secret_token = 'a' * 100
29
- end
30
-
31
- class HelloController < ActionController::Base
32
- def world
33
- render :text => "Hello world!", :layout => false
34
- end
35
- end
36
-
37
- TestApp.initialize!
38
-
39
- # We have to define this class because it's hard-coded into the definition of
40
- # ActiveSupport::TestCase, which will load regardless of whether we load
41
- # ActiveRecord.
42
- module ActiveRecord
43
- class Model
44
- end
45
- end
46
-
47
- ## Examples
48
-
49
- class EmptyPresenter < Keynote::Presenter
50
- end
51
-
52
- class NormalPresenter < Keynote::Presenter
53
- presents :model
54
-
55
- def some_bad_js
56
- "<script>alert('pwnt');</script>"
57
- end
58
-
59
- def some_bad_html
60
- build_html do
61
- div { text some_bad_js }
62
- div { some_bad_js }
63
- div some_bad_js
64
- end
65
- end
66
- end
67
-
68
- class Normal
69
- end
70
-
71
- module Keynote
72
- class NestedPresenter < Keynote::Presenter
73
- presents :model
74
-
75
- def generate_div
76
- build_html do
77
- div.hi! do
78
- link_to '#', 'Hello'
79
- end
80
- end
81
- end
82
- end
83
-
84
- class Nested
85
- end
86
- end
87
-
88
- class CombinedPresenter < Keynote::Presenter
89
- presents :model_1, :model_2
90
- end
data/spec/inline_spec.rb DELETED
@@ -1,157 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require "helper"
4
- require "fileutils"
5
- require "slim"
6
- require "haml"
7
- require "haml/template"
8
-
9
- module Keynote
10
- describe Inline do
11
- let(:presenter) { InlineUser.new(:view) }
12
-
13
- def clean_whitespace(str)
14
- str.gsub(/\s/, "")
15
- end
16
-
17
- class InlineUser < Keynote::Presenter
18
- extend Keynote::Inline
19
- inline :slim, :haml
20
-
21
- def simple_template
22
- erb
23
- # Here's some math: <%= 2 + 2 %>
24
- end
25
-
26
- def ivars
27
- @greetee = "world"
28
- erb
29
- # Hello <%= @greetee %>!
30
- end
31
-
32
- def locals_from_hash
33
- erb local: "H"
34
- # Local <%= local %>
35
- end
36
-
37
- def locals_from_binding
38
- local = "H"
39
- erb binding
40
- # Local <%= local %>
41
- end
42
-
43
- def method_calls
44
- erb
45
- # <%= locals_from_hash %>
46
- # <%= locals_from_binding %>
47
- end
48
-
49
- def error_handling
50
- erb
51
- # <% raise "UH OH" %>
52
- end
53
-
54
- def fix_indentation
55
- slim
56
- # .indented_slightly
57
- # - (2..4).each do |i|
58
- # ' #{i} times
59
- end
60
-
61
- def erb_escaping
62
- raw = erb
63
- # <%= "<script>alert(1);</script>" %>
64
- escaped = erb
65
- # <%= "<script>alert(1);</script>".html_safe %>
66
- raw + escaped
67
- end
68
-
69
- def slim_escaping
70
- raw = slim
71
- # = "<script>alert(1);</script>"
72
- escaped = slim
73
- # = "<script>alert(1);</script>".html_safe
74
- raw + escaped
75
- end
76
-
77
- def haml_escaping
78
- raw = haml
79
- # = "<script>alert(1);</script>"
80
- escaped = haml
81
- # = "<script>alert(1);</script>".html_safe
82
- raw + escaped
83
- end
84
- end
85
-
86
- before do
87
- Keynote::Inline::Cache.reset
88
- end
89
-
90
- it "should render a template" do
91
- presenter.simple_template.strip.must_equal "Here's some math: 4"
92
- end
93
-
94
- it "should see instance variables from the presenter" do
95
- presenter.ivars.strip.must_equal "Hello world!"
96
- end
97
-
98
- it "should see locals passed in as a hash" do
99
- presenter.locals_from_hash.strip.must_equal "Local H"
100
- end
101
-
102
- it "should see locals passed in as a binding" do
103
- presenter.locals_from_binding.strip.must_equal "Local H"
104
- end
105
-
106
- it "should be able to call other methods from the same object" do
107
- presenter.method_calls.strip.squeeze(" ").must_equal "Local H\nLocal H"
108
- end
109
-
110
- it "should handle errors relatively gracefully" do
111
- begin
112
- presenter.error_handling
113
- rescue => e
114
- end
115
-
116
- e.must_be_instance_of ActionView::Template::Error
117
-
118
- if e.respond_to?(:original_exception)
119
- e.original_exception.must_be_instance_of RuntimeError
120
- e.original_exception.message.must_equal "UH OH"
121
- else
122
- e.cause.must_be_instance_of RuntimeError
123
- e.cause.message.must_equal "UH OH"
124
- end
125
- end
126
-
127
- it "should remove leading indentation" do
128
- presenter.fix_indentation.must_equal \
129
- "<div class=\"indented_slightly\">2 times 3 times 4 times </div>"
130
- end
131
-
132
- it "should escape HTML by default" do
133
- unescaped = "<script>alert(1);</script>"
134
- escaped = unescaped.gsub(/</, "&lt;").gsub(/>/, "&gt;")
135
- escaped2 = escaped.gsub(/\//, "&#47;") # for Slim w/ Rails > 3.0 (??)
136
-
137
- clean_whitespace(presenter.erb_escaping).must_equal escaped + unescaped
138
- clean_whitespace(presenter.haml_escaping).must_equal escaped + unescaped
139
-
140
- [escaped + unescaped, escaped2 + unescaped].must_include \
141
- clean_whitespace(presenter.slim_escaping)
142
- end
143
-
144
- it "should see updates after the file is reloaded" do
145
- presenter.simple_template.strip.must_equal "Here's some math: 4"
146
-
147
- Keynote::Inline::Cache.
148
- any_instance.stubs(:read_template).returns("HELLO")
149
-
150
- presenter.simple_template.strip.must_equal "Here's some math: 4"
151
-
152
- FileUtils.touch __FILE__
153
-
154
- presenter.simple_template.must_equal "HELLO"
155
- end
156
- end
157
- end
data/spec/keynote_spec.rb DELETED
@@ -1,130 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require 'helper'
4
-
5
- describe Keynote do
6
- let(:view) { Object.new }
7
-
8
- describe "with a normal presenter" do
9
- let(:model) { Normal.new }
10
-
11
- it "should find and instantiate implicitly" do
12
- p = Keynote.present(view, model)
13
-
14
- p.wont_be_nil
15
- p.must_be_instance_of NormalPresenter
16
-
17
- p.view.must_equal view
18
- p.model.must_equal model
19
- end
20
-
21
- it "should find and instantiate explicitly" do
22
- p = Keynote.present(view, :normal, 'hello')
23
-
24
- p.wont_be_nil
25
- p.must_be_instance_of NormalPresenter
26
-
27
- p.view.must_equal view
28
- p.model.must_equal 'hello'
29
- end
30
-
31
- it "should take a block and pass the presenter into it" do
32
- m = mock()
33
- m.expects(:block_yielded)
34
-
35
- Keynote.present(view, :normal, 'hello') do |p|
36
- m.block_yielded
37
-
38
- p.wont_be_nil
39
- p.must_be_instance_of NormalPresenter
40
-
41
- p.view.must_equal view
42
- p.model.must_equal 'hello'
43
- end
44
- end
45
-
46
- it "should integrate with Rumble" do
47
- p = Keynote.present(view, model)
48
- rx = /<div>&lt;script&gt;alert\(/
49
-
50
- p.some_bad_html.scan(rx).count.must_equal 3
51
- end
52
- end
53
-
54
- describe "with a nested presenter" do
55
- let(:model) { Keynote::Nested.new }
56
-
57
- it "should find and instantiate implicitly" do
58
- p = Keynote.present(view, model)
59
-
60
- p.wont_be_nil
61
- p.must_be_instance_of Keynote::NestedPresenter
62
-
63
- p.view.must_equal view
64
- p.model.must_equal model
65
- end
66
-
67
- it "should find and instantiate explicitly" do
68
- p = Keynote.present(view, "keynote/nested", 'hello')
69
-
70
- p.wont_be_nil
71
- p.must_be_instance_of Keynote::NestedPresenter
72
-
73
- p.view.must_equal view
74
- p.model.must_equal 'hello'
75
- end
76
- end
77
-
78
- describe "caching" do
79
- describe "when there is a view context" do
80
- let(:view_2) { Object.new }
81
-
82
- it "should cache based on the models" do
83
- model_1 = Normal.new
84
- model_2 = Normal.new
85
-
86
- presented_1 = Keynote.present(view, model_1)
87
- presented_2 = Keynote.present(view, model_1)
88
-
89
- presented_1.must_be :equal?, presented_2
90
-
91
- presented_3 = Keynote.present(view, :combined, model_1, model_2)
92
- presented_4 = Keynote.present(view, :combined, model_1, model_2)
93
- presented_5 = Keynote.present(view, :combined, model_2, model_1)
94
-
95
- presented_3.wont_be :equal?, presented_1
96
- presented_3.must_be :equal?, presented_4
97
- presented_3.wont_be :equal?, presented_5
98
- end
99
-
100
- it "should cache even if there are no models" do
101
- presenter_1 = Keynote.present(view, :empty)
102
- presenter_2 = Keynote.present(view, :empty)
103
-
104
- presenter_1.must_be :equal?, presenter_2
105
- end
106
-
107
- it "should be scoped to the specific view context" do
108
- model = Normal.new
109
-
110
- presenter_1 = Keynote.present(view, model)
111
- presenter_1.view.must_equal view
112
-
113
- presenter_2 = Keynote.present(view_2, model)
114
- presenter_2.wont_be :equal?, presenter_1
115
- presenter_2.view.must_equal view_2
116
- end
117
- end
118
-
119
- describe "when there's no view context" do
120
- it "shouldn't cache" do
121
- model = Normal.new
122
-
123
- presented_1 = Keynote.present(nil, model)
124
- presented_2 = Keynote.present(nil, model)
125
-
126
- presented_1.wont_be :equal?, presented_2
127
- end
128
- end
129
- end
130
- end