keynote 0.2.0pre1 → 0.2.0pre2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of keynote might be problematic. Click here for more details.

@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "4.0.0rc1"
6
+ gem "activerecord-deprecated_finders", "~> 1.0.2"
7
+ gem "journey", "~> 1.0.4"
8
+
9
+ gemspec :path=>"../"
@@ -0,0 +1,120 @@
1
+ PATH
2
+ remote: /Users/rfitz/src/keynote
3
+ specs:
4
+ keynote (0.2.0pre1)
5
+ rails (>= 3.0.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ actionmailer (4.0.0.rc1)
11
+ actionpack (= 4.0.0.rc1)
12
+ mail (~> 2.5.3)
13
+ actionpack (4.0.0.rc1)
14
+ activesupport (= 4.0.0.rc1)
15
+ builder (~> 3.1.0)
16
+ erubis (~> 2.7.0)
17
+ rack (~> 1.5.2)
18
+ rack-test (~> 0.6.2)
19
+ activemodel (4.0.0.rc1)
20
+ activesupport (= 4.0.0.rc1)
21
+ builder (~> 3.1.0)
22
+ activerecord (4.0.0.rc1)
23
+ activemodel (= 4.0.0.rc1)
24
+ activerecord-deprecated_finders (~> 1.0.2)
25
+ activesupport (= 4.0.0.rc1)
26
+ arel (~> 4.0.0)
27
+ activerecord-deprecated_finders (1.0.2)
28
+ activesupport (4.0.0.rc1)
29
+ i18n (~> 0.6, >= 0.6.4)
30
+ minitest (~> 4.2)
31
+ multi_json (~> 1.3)
32
+ thread_safe (~> 0.1)
33
+ tzinfo (~> 0.3.37)
34
+ appraisal (0.5.2)
35
+ bundler
36
+ rake
37
+ arel (4.0.0)
38
+ atomic (1.1.8)
39
+ builder (3.1.4)
40
+ coderay (1.0.9)
41
+ erubis (2.7.0)
42
+ haml (4.0.2)
43
+ tilt
44
+ hike (1.2.2)
45
+ i18n (0.6.4)
46
+ journey (1.0.4)
47
+ mail (2.5.3)
48
+ i18n (>= 0.4.0)
49
+ mime-types (~> 1.16)
50
+ treetop (~> 1.4.8)
51
+ metaclass (0.0.1)
52
+ method_source (0.8.1)
53
+ mime-types (1.23)
54
+ minitest (4.7.4)
55
+ mocha (0.13.3)
56
+ metaclass (~> 0.0.1)
57
+ multi_json (1.7.2)
58
+ polyglot (0.3.3)
59
+ pry (0.9.12.1)
60
+ coderay (~> 1.0.5)
61
+ method_source (~> 0.8)
62
+ slop (~> 3.4)
63
+ rack (1.5.2)
64
+ rack-test (0.6.2)
65
+ rack (>= 1.0)
66
+ rails (4.0.0.rc1)
67
+ actionmailer (= 4.0.0.rc1)
68
+ actionpack (= 4.0.0.rc1)
69
+ activerecord (= 4.0.0.rc1)
70
+ activesupport (= 4.0.0.rc1)
71
+ bundler (>= 1.3.0, < 2.0)
72
+ railties (= 4.0.0.rc1)
73
+ sprockets-rails (~> 2.0.0.rc4)
74
+ railties (4.0.0.rc1)
75
+ actionpack (= 4.0.0.rc1)
76
+ activesupport (= 4.0.0.rc1)
77
+ rake (>= 0.8.7)
78
+ thor (>= 0.18.1, < 2.0)
79
+ rake (10.0.4)
80
+ redcarpet (2.2.2)
81
+ slim (1.0.1)
82
+ temple (~> 0.3.0)
83
+ tilt (~> 1.2)
84
+ slop (3.4.4)
85
+ sprockets (2.9.3)
86
+ hike (~> 1.2)
87
+ multi_json (~> 1.0)
88
+ rack (~> 1.0)
89
+ tilt (~> 1.1, != 1.3.0)
90
+ sprockets-rails (2.0.0.rc4)
91
+ actionpack (>= 3.0)
92
+ activesupport (>= 3.0)
93
+ sprockets (~> 2.8)
94
+ temple (0.3.5)
95
+ thor (0.18.1)
96
+ thread_safe (0.1.0)
97
+ atomic
98
+ tilt (1.4.0)
99
+ treetop (1.4.12)
100
+ polyglot
101
+ polyglot (>= 0.3.1)
102
+ tzinfo (0.3.37)
103
+ yard (0.8.6.1)
104
+
105
+ PLATFORMS
106
+ ruby
107
+
108
+ DEPENDENCIES
109
+ activerecord-deprecated_finders (~> 1.0.2)
110
+ appraisal
111
+ haml
112
+ journey (~> 1.0.4)
113
+ keynote!
114
+ minitest
115
+ mocha (~> 0.13.3)
116
+ pry
117
+ rails (= 4.0.0rc1)
118
+ redcarpet
119
+ slim
120
+ yard
@@ -23,8 +23,11 @@ Gem::Specification.new do |gem|
23
23
 
24
24
  gem.add_development_dependency 'appraisal'
25
25
  gem.add_development_dependency 'minitest'
26
- gem.add_development_dependency 'mocha'
26
+ gem.add_development_dependency 'mocha', '~> 0.13.3'
27
27
  gem.add_development_dependency 'pry'
28
28
  gem.add_development_dependency 'redcarpet'
29
29
  gem.add_development_dependency 'yard'
30
+
31
+ gem.add_development_dependency 'slim'
32
+ gem.add_development_dependency 'haml'
30
33
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "keynote/version"
4
4
  require "keynote/rumble"
5
+ require "keynote/inline"
5
6
  require "keynote/presenter"
6
7
  require "keynote/controller"
7
8
  require "keynote/helper"
@@ -0,0 +1,207 @@
1
+ # encoding: UTF-8
2
+
3
+ require "thread"
4
+
5
+ module Keynote
6
+ # The `Inline` mixin lets you write inline templates as comments inside the
7
+ # body of a presenter method. You can use any template language supported by
8
+ # Rails.
9
+ #
10
+ # ## The `inline` method
11
+ #
12
+ # First, you have to declare what template languages you want to use by
13
+ # calling the {Keynote::Inline#inline} method on a presenter class:
14
+ #
15
+ # class MyPresenter < Keynote::Presenter
16
+ # presents :user, :account
17
+ # inline :haml
18
+ # end
19
+ #
20
+ # This defines a `#haml` instance method on the `MyPresenter` class.
21
+ #
22
+ # If you want to make inline templates available to all of your presenters,
23
+ # you can add an initializer like this to your application:
24
+ #
25
+ # class Keynote::Presenter
26
+ # inline :erb, :haml, :slim
27
+ # end
28
+ #
29
+ # This will add `#erb`, `#haml`, and `#slim` instance methods to all of your
30
+ # presenters.
31
+ #
32
+ # ## Basic usage
33
+ #
34
+ # After defining one or more instance methods by calling `inline`, you can
35
+ # generate HTML by calling one of those methods and immediately following it
36
+ # with a block of comments containing your template:
37
+ #
38
+ # def link
39
+ # erb
40
+ # # <%= link_to user_url(current_user) do %>
41
+ # # <%= image_tag("image1.jpg") %>
42
+ # # <%= image_tag("image2.jpg") %>
43
+ # # <% end %>
44
+ # end
45
+ #
46
+ # Calling this method renders the ERB template, including passing the calls
47
+ # to `link_to`, `user_url`, `current_user`, and `image_tag` back to the
48
+ # presenter object (and then to the view).
49
+ #
50
+ # ## Passing variables
51
+ #
52
+ # There are a couple of different ways to pass local variables into an inline
53
+ # template. The easiest is to pass the `binding` object into the template
54
+ # method, giving access to all local variables:
55
+ #
56
+ # def local_binding
57
+ # x = 1
58
+ # y = 2
59
+ #
60
+ # erb binding
61
+ # # <%= x + y %>
62
+ # end
63
+ #
64
+ # You can also pass a hash of variable names and values instead:
65
+ #
66
+ # def local_binding
67
+ # erb x: 1, y: 2
68
+ # # <%= x + y %>
69
+ # end
70
+ module Inline
71
+ # For each template format given as a parameter, add an instance method
72
+ # that can be called to render an inline template in that format. Any
73
+ # file extension supported by Rails is a valid parameter.
74
+ # @example
75
+ # class UserPresenter < Keynote::Presenter
76
+ # presents :user
77
+ # inline :haml
78
+ #
79
+ # def header
80
+ # full_name = "#{user.first_name} #{user.last_name}"
81
+ #
82
+ # haml binding
83
+ # # div#header
84
+ # # h1= full_name
85
+ # # h3= user.most_recent_status
86
+ # end
87
+ # end
88
+ def inline(*formats)
89
+ require "action_view"
90
+ require "action_view/context"
91
+
92
+ Array(formats).each do |format|
93
+ define_method format do |locals = {}|
94
+ Renderer.new(self, locals, caller(1)[0], format).render
95
+ end
96
+ end
97
+ end
98
+
99
+ # @private
100
+ class Renderer
101
+ def initialize(presenter, locals, caller_line, format)
102
+ @presenter = presenter
103
+ @locals = extract_locals(locals)
104
+ @template = Cache.fetch(*parse_caller(caller_line), format, @locals)
105
+ end
106
+
107
+ def render
108
+ @template.render(@presenter, @locals)
109
+ end
110
+
111
+ private
112
+
113
+ def extract_locals(locals)
114
+ return locals unless locals.is_a?(Binding)
115
+
116
+ Hash[locals.eval("local_variables").map do |local|
117
+ [local, locals.eval(local.to_s)]
118
+ end]
119
+ end
120
+
121
+ def parse_caller(caller_line)
122
+ file, rest = caller_line.split ":", 2
123
+ line, _ = rest.split " ", 2
124
+
125
+ [file.strip, line.to_i]
126
+ end
127
+ end
128
+
129
+ # @private
130
+ class Cache
131
+ COMMENTED_LINE = /^\s*#(.*)$/
132
+
133
+ def self.fetch(source_file, line, format, locals)
134
+ instance = (Thread.current[:_keynote_template_cache] ||= Cache.new)
135
+ instance.fetch(source_file, line, format, locals)
136
+ end
137
+
138
+ def self.reset
139
+ Thread.current[:_keynote_template_cache] = nil
140
+ end
141
+
142
+ def initialize
143
+ @cache = {}
144
+ end
145
+
146
+ def fetch(source_file, line, format, locals)
147
+ local_names = locals.keys.sort
148
+ cache_key = ["#{source_file}:#{line}", *local_names].freeze
149
+ new_mtime = File.mtime(source_file).to_f
150
+
151
+ template, mtime = @cache[cache_key]
152
+
153
+ if new_mtime != mtime
154
+ source = read_template(source_file, line)
155
+
156
+ template = ActionView::Template.new(source, cache_key[0],
157
+ handler_for_format(format), locals: local_names)
158
+
159
+ @cache[cache_key] = [template, new_mtime]
160
+ end
161
+
162
+ template
163
+ end
164
+
165
+ private
166
+
167
+ def read_template(source_file, line)
168
+ result = ""
169
+
170
+ File.foreach(source_file).drop(line).each do |line|
171
+ if line =~ COMMENTED_LINE
172
+ result << $1
173
+ else
174
+ break
175
+ end
176
+ end
177
+
178
+ unindent result
179
+ end
180
+
181
+ # Borrowed from Pry, which borrowed it from Python.
182
+ def unindent(text, left_padding = 0)
183
+ margin = text.scan(/^[ \t]*(?=[^ \t\n])/).inject do |current_margin, next_indent|
184
+ if next_indent.start_with?(current_margin)
185
+ current_margin
186
+ elsif current_margin.start_with?(next_indent)
187
+ next_indent
188
+ else
189
+ ""
190
+ end
191
+ end
192
+
193
+ text.gsub(/^#{margin}/, ' ' * left_padding)
194
+ end
195
+
196
+ if Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR == 0
197
+ def handler_for_format(format)
198
+ ActionView::Template.handler_class_for_extension(format.to_s)
199
+ end
200
+ else
201
+ def handler_for_format(format)
202
+ ActionView::Template.handler_for_extension(format.to_s)
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
@@ -9,6 +9,8 @@ module Keynote
9
9
  include Keynote::Rumble
10
10
 
11
11
  class << self
12
+ attr_writer :object_names
13
+
12
14
  # Define the names and number of the objects presented by this class.
13
15
  # This replaces the default one-parameter constructor with one that takes
14
16
  # an extra parameter for each presented object.
@@ -29,6 +31,8 @@ module Keynote
29
31
  # presenter.author # == @some_user
30
32
  #
31
33
  def presents(*objects)
34
+ self.object_names = objects.dup
35
+
32
36
  objects.unshift :view
33
37
  attr_reader *objects
34
38
 
@@ -47,6 +51,14 @@ module Keynote
47
51
  def use_html_5_tags
48
52
  Rumble.use_html_5_tags(self)
49
53
  end
54
+
55
+ # List the object names this presenter wraps. The default is an empty
56
+ # array; calling `presents :foo, :bar` in the presenter's class body will
57
+ # cause `object_names` to return `[:foo, :bar]`.
58
+ # @return [Array<Symbol>]
59
+ def object_names
60
+ @object_names ||= []
61
+ end
50
62
  end
51
63
 
52
64
  # @private (used by Keynote::Cache to keep the view context up-to-date)
@@ -67,6 +79,18 @@ module Keynote
67
79
  end
68
80
  alias k present
69
81
 
82
+ # @private
83
+ def inspect
84
+ objects = self.class.object_names
85
+ render = proc { |name| "#{name}: #{send(name).inspect}" }
86
+
87
+ if objects.any?
88
+ "#<#{self.class} #{objects.map(&render).join(", ")}>"
89
+ else
90
+ "#<#{self.class}>"
91
+ end
92
+ end
93
+
70
94
  # @private
71
95
  def respond_to_missing?(method_name, include_private = true)
72
96
  @view.respond_to?(method_name, true)
@@ -89,6 +113,13 @@ module Keynote
89
113
  end
90
114
  end
91
115
 
116
+ # @private
117
+ # We have to make a logger method available so that ActionView::Template
118
+ # can safely treat a presenter as a view object.
119
+ def logger
120
+ Rails.logger
121
+ end
122
+
92
123
  private
93
124
 
94
125
  # We have to explicitly proxy `#capture` because ActiveSupport creates a
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Keynote
4
4
  # @private
5
- VERSION = "0.2.0pre1"
5
+ VERSION = "0.2.0pre2"
6
6
  end
@@ -0,0 +1,72 @@
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
+ inline :erb
12
+
13
+ def my_string
14
+ "a" + "b" + "c"
15
+ end
16
+
17
+ def rumble
18
+ a_local = 1000
19
+
20
+ build_html do
21
+ div.foobar.baz! do
22
+ p { my_string }
23
+ p { a_local }
24
+ end
25
+ end
26
+ end
27
+
28
+ def erb_hash
29
+ a_local = 1000
30
+
31
+ erb a_local: a_local
32
+ # <div class="foobar" id="baz">
33
+ # <p><%= my_string %></p>
34
+ # <p><%= a_local %></p>
35
+ # </div>
36
+ end
37
+
38
+ def erb_binding
39
+ a_local = 1000
40
+
41
+ erb binding
42
+ # <div class="foobar" id="baz">
43
+ # <p><%= my_string %></p>
44
+ # <p><%= a_local %></p>
45
+ # </div>
46
+ end
47
+
48
+ def raw_erb_template
49
+ source = %{
50
+ <div class="foobar" id="baz">
51
+ <p><%= my_string %></p>
52
+ <p><%= a_local %></p>
53
+ </div>
54
+ }
55
+ template = ActionView::Template.new(
56
+ source, "raw_erb_template",
57
+ ActionView::Template.handler_for_extension(:erb),
58
+ locals: [:a_local]
59
+ )
60
+ TESTS.times { template.render(self, a_local: 1000) }
61
+ end
62
+ end
63
+
64
+ TESTS = 1_000
65
+ presenter = MyPresenter.new(:view)
66
+
67
+ Benchmark.bmbm do |results|
68
+ results.report("rumble") { TESTS.times { presenter.rumble } }
69
+ results.report("erb_hash") { TESTS.times { presenter.erb_hash } }
70
+ results.report("erb_binding") { TESTS.times { presenter.erb_binding } }
71
+ results.report("raw_erb_template") { presenter.raw_erb_template }
72
+ end