keynote 1.1.0 → 2.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 (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