keynote 0.0.1
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.
- data/.gitignore +19 -0
- data/.yardopts +4 -0
- data/Appraisals +20 -0
- data/Gemfile +4 -0
- data/LICENSE +27 -0
- data/README.md +8 -0
- data/Rakefile +17 -0
- data/keynote.gemspec +26 -0
- data/lib/generators/presenter_generator.rb +70 -0
- data/lib/generators/templates/keynote_mini_test_spec.rb +11 -0
- data/lib/generators/templates/keynote_mini_test_unit.rb +11 -0
- data/lib/generators/templates/keynote_presenter.rb +7 -0
- data/lib/generators/templates/keynote_rspec.rb +5 -0
- data/lib/generators/templates/keynote_test_unit.rb +11 -0
- data/lib/keynote.rb +65 -0
- data/lib/keynote/cache.rb +53 -0
- data/lib/keynote/controller.rb +14 -0
- data/lib/keynote/helper.rb +14 -0
- data/lib/keynote/presenter.rb +90 -0
- data/lib/keynote/railtie.rb +40 -0
- data/lib/keynote/rumble.rb +369 -0
- data/lib/keynote/testing/minitest_rails.rake +2 -0
- data/lib/keynote/testing/minitest_rails.rb +22 -0
- data/lib/keynote/testing/rspec.rb +16 -0
- data/lib/keynote/testing/test_unit.rb +6 -0
- data/lib/keynote/version.rb +6 -0
- data/spec/generator_spec.rb +106 -0
- data/spec/helper.rb +84 -0
- data/spec/keynote_spec.rb +98 -0
- data/spec/presenter_spec.rb +116 -0
- data/spec/railtie_spec.rb +32 -0
- data/spec/rumble_spec.rb +201 -0
- data/spec/test_case_spec.rb +12 -0
- metadata +204 -0
data/.gitignore
ADDED
data/.yardopts
ADDED
data/Appraisals
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
appraise "rails30" do
|
2
|
+
gem "rails", "3.0.15"
|
3
|
+
end
|
4
|
+
|
5
|
+
appraise "rails31" do
|
6
|
+
gem "rails", "3.1.6"
|
7
|
+
end
|
8
|
+
|
9
|
+
appraise "rails32" do
|
10
|
+
gem "rails", "3.2.6"
|
11
|
+
end
|
12
|
+
|
13
|
+
appraise "rails4" do
|
14
|
+
gem "rails",
|
15
|
+
:git => "git@github.com:rails/rails.git"
|
16
|
+
gem "activerecord-deprecated_finders",
|
17
|
+
:git => "git@github.com:rails/activerecord-deprecated_finders"
|
18
|
+
gem "journey",
|
19
|
+
:git => "git@github.com:rails/journey"
|
20
|
+
end
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2012 Ryan Fitzgerald
|
2
|
+
|
3
|
+
Includes a modified version of Rumble, which is (c) 2011 Magnus Holm.
|
4
|
+
|
5
|
+
Some Rails integration code inspired by Draper, which is (c) 2011-2012 Jeff
|
6
|
+
Casimir and other contributors.
|
7
|
+
|
8
|
+
MIT License
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
11
|
+
a copy of this software and associated documentation files (the
|
12
|
+
"Software"), to deal in the Software without restriction, including
|
13
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
14
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
15
|
+
permit persons to whom the Software is furnished to do so, subject to
|
16
|
+
the following conditions:
|
17
|
+
|
18
|
+
The above copyright notice and this permission notice shall be
|
19
|
+
included in all copies or substantial portions of the Software.
|
20
|
+
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
22
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
23
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
24
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
25
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
26
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
27
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
require "appraisal"
|
6
|
+
require "yard"
|
7
|
+
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs.concat %w(keynote spec)
|
10
|
+
t.pattern = "spec/*_spec.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
YARD::Rake::YardocTask.new do |t|
|
14
|
+
t.files = ['lib/**/*.rb']
|
15
|
+
end
|
16
|
+
|
17
|
+
task :default => [:test]
|
data/keynote.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/keynote/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Ryan Fitzgerald"]
|
6
|
+
gem.email = ["rwfitzge@gmail.com"]
|
7
|
+
gem.description = %q{}
|
8
|
+
gem.summary = %q{}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "keynote"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Keynote::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'rails', '>= 3.0.0'
|
19
|
+
|
20
|
+
gem.add_development_dependency 'appraisal'
|
21
|
+
gem.add_development_dependency 'minitest'
|
22
|
+
gem.add_development_dependency 'mocha'
|
23
|
+
gem.add_development_dependency 'pry'
|
24
|
+
gem.add_development_dependency 'redcarpet'
|
25
|
+
gem.add_development_dependency 'yard'
|
26
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Rails::Generators
|
4
|
+
class PresenterGenerator < Rails::Generators::NamedBase
|
5
|
+
desc "This generator creates a Keynote::Presenter subclass in " \
|
6
|
+
"app/presenters."
|
7
|
+
|
8
|
+
argument :targets, :type => :array, :default => []
|
9
|
+
|
10
|
+
check_class_collision :suffix => 'Presenter'
|
11
|
+
source_root File.expand_path('../templates', __FILE__)
|
12
|
+
|
13
|
+
def create_presenter_file
|
14
|
+
template 'keynote_presenter.rb',
|
15
|
+
File.join('app/presenters', class_path, "#{file_name}_presenter.rb")
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_test_file
|
19
|
+
case Rails.application.config.generators.rails[:test_framework]
|
20
|
+
when :rspec
|
21
|
+
template 'keynote_rspec.rb', rspec_path
|
22
|
+
when :test_unit
|
23
|
+
template 'keynote_test_unit.rb', test_unit_path
|
24
|
+
when :mini_test
|
25
|
+
if Rails.application.config.generators.mini_test[:spec]
|
26
|
+
template 'keynote_mini_test_spec.rb', mini_test_path
|
27
|
+
else
|
28
|
+
template 'keynote_mini_test_unit.rb', mini_test_path
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def rspec_path
|
36
|
+
File.join(
|
37
|
+
'spec/presenters', class_path, "#{file_name}_presenter_spec.rb")
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_unit_path
|
41
|
+
File.join(
|
42
|
+
'test/unit/presenters', class_path, "#{file_name}_presenter_test.rb")
|
43
|
+
end
|
44
|
+
|
45
|
+
def mini_test_path
|
46
|
+
File.join(
|
47
|
+
'test/presenters', class_path, "#{file_name}_presenter_test.rb")
|
48
|
+
end
|
49
|
+
|
50
|
+
def target_list
|
51
|
+
targets.map { |t| ":#{t}" }.join(', ')
|
52
|
+
end
|
53
|
+
|
54
|
+
def view_and_target_list
|
55
|
+
if targets.any?
|
56
|
+
['view', target_list].join(', ')
|
57
|
+
else
|
58
|
+
'view'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if ::Rails.version.to_f < 3.1
|
63
|
+
protected
|
64
|
+
# This methods doesn't exist in Rails 3.0
|
65
|
+
def module_namespacing
|
66
|
+
yield if block_given?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/keynote.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "keynote/version"
|
4
|
+
require "keynote/rumble"
|
5
|
+
require "keynote/presenter"
|
6
|
+
require "keynote/controller"
|
7
|
+
require "keynote/helper"
|
8
|
+
require "keynote/railtie"
|
9
|
+
require "keynote/cache"
|
10
|
+
|
11
|
+
module Keynote
|
12
|
+
class << self
|
13
|
+
# Create or retrieve a presenter wrapping zero or more objects.
|
14
|
+
#
|
15
|
+
# The first parameter is a Rails view context, but you'll usually access
|
16
|
+
# this method through `Keynote::Helper#present`,
|
17
|
+
# `Keynote::Controller#present`, or `Keynote::Presenter#present`, all of
|
18
|
+
# which handle the view context parameter automatically.
|
19
|
+
#
|
20
|
+
# @see Keynote::Helper#present
|
21
|
+
# @see Keynote::Controller#present
|
22
|
+
# @see Keynote::Presenter#present
|
23
|
+
#
|
24
|
+
# @overload present(*objects)
|
25
|
+
# Return a presenter wrapping the given objects. The type of the
|
26
|
+
# presenter will be inferred from the type of the first object.
|
27
|
+
# @example
|
28
|
+
# present(view, MyModel.new) # => #<MyModelPresenter:0x0001>
|
29
|
+
# @param [ActionView::Base] view
|
30
|
+
# @param [Array] objects
|
31
|
+
# @return [Keynote::Presenter]
|
32
|
+
#
|
33
|
+
# @overload present(view, presenter_name, *objects)
|
34
|
+
# Return a presenter wrapping the given objects. The type of the
|
35
|
+
# presenter is specified in underscore form by the first parameter.
|
36
|
+
# @example
|
37
|
+
# present(view, :foo_bar, MyModel.new) # => #<FooBarPresenter:0x0002>
|
38
|
+
# @param [ActionView::Base] view
|
39
|
+
# @param [Symbol] presenter_name
|
40
|
+
# @param [Array] objects
|
41
|
+
# @return [Keynote::Presenter]
|
42
|
+
#
|
43
|
+
def present(view, *objects)
|
44
|
+
if objects[0].is_a?(Symbol)
|
45
|
+
name = objects.shift
|
46
|
+
else
|
47
|
+
name = presenter_name_from_object(objects[0])
|
48
|
+
end
|
49
|
+
|
50
|
+
Cache.fetch(name, view, *objects) do
|
51
|
+
presenter_from_name(name).new(view, *objects)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def presenter_name_from_object(object)
|
58
|
+
object.class.to_s.underscore
|
59
|
+
end
|
60
|
+
|
61
|
+
def presenter_from_name(name)
|
62
|
+
"#{name.to_s.camelize}Presenter".constantize
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Keynote
|
4
|
+
# `Keynote::Cache` memoizes presenter instances, reducing the overhead of
|
5
|
+
# calling `Keynote.present` repeatedly with the same parameters.
|
6
|
+
module Cache
|
7
|
+
end
|
8
|
+
|
9
|
+
class << Cache
|
10
|
+
# Return a cached presenter for the given parameters, or yield and cache
|
11
|
+
# the block's return value for next time.
|
12
|
+
#
|
13
|
+
# The cached presenters are stored in an instance variable on the first of
|
14
|
+
# the given objects, so they'll be garbage-collected when the associated
|
15
|
+
# models go out of scope.
|
16
|
+
#
|
17
|
+
# @param [Symbol] name
|
18
|
+
# @param [ActionView::Base] view
|
19
|
+
# @param [Array] objects
|
20
|
+
# @return [Keynote::Presenter]
|
21
|
+
def fetch(name, view, *objects)
|
22
|
+
first = objects.shift
|
23
|
+
|
24
|
+
# If this is a zero-object presenter, we have to just instantiate it.
|
25
|
+
if first.nil?
|
26
|
+
return(yield)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Otherwise, we can initialize our cache on the first of the presented
|
30
|
+
# objects.
|
31
|
+
if (cache = first.instance_variable_get(:@_keynote_cache)).nil?
|
32
|
+
cache = {}
|
33
|
+
first.instance_variable_set(:@_keynote_cache, cache)
|
34
|
+
end
|
35
|
+
|
36
|
+
# We key each entry by the name of the presenter and the object_id of
|
37
|
+
# each of the other objects involved.
|
38
|
+
key = [name, *objects.map(&:object_id)]
|
39
|
+
|
40
|
+
# If we're using a cached presenter, update the view to match the one the
|
41
|
+
# user passed in (in case the presenter was originally instantiated in
|
42
|
+
# the controller or whatever).
|
43
|
+
if (presenter = cache[key])
|
44
|
+
presenter.view = view
|
45
|
+
else
|
46
|
+
presenter = yield
|
47
|
+
cache[key] = presenter
|
48
|
+
end
|
49
|
+
|
50
|
+
presenter
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Keynote
|
4
|
+
# `Keynote::Controller` is mixed into `ActionController::Base` and
|
5
|
+
# `ActionMailer::Base`, providing a `present` method for instantiating
|
6
|
+
# presenters.
|
7
|
+
module Controller
|
8
|
+
# Instantiate a presenter.
|
9
|
+
# @see Keynote.present
|
10
|
+
def present(*objects)
|
11
|
+
Keynote.present(view_context, *objects)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Keynote
|
4
|
+
# `Keynote::Helper` is mixed into `ActionView::Base`, providing a `present`
|
5
|
+
# method (aliased to `p`) for instantiating presenters.
|
6
|
+
module Helper
|
7
|
+
# Instantiate a presenter.
|
8
|
+
# @see Keynote.present
|
9
|
+
def present(*objects)
|
10
|
+
Keynote.present(self, *objects)
|
11
|
+
end
|
12
|
+
alias p present
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Keynote
|
4
|
+
class Presenter
|
5
|
+
include Keynote::Rumble
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# Define the names and number of the objects presented by this class.
|
9
|
+
# This replaces the default one-parameter constructor with one that takes
|
10
|
+
# an extra parameter for each presented object.
|
11
|
+
#
|
12
|
+
# @param [Array<Symbol>] objects One symbol for each of the models that
|
13
|
+
# will be required to instantiate this presenter. Each symbol will be
|
14
|
+
# used as the accessor and instance variable name for its associated
|
15
|
+
# parameter, but an object of any class can be given for any parameter.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# class PostPresenter
|
19
|
+
# presents :blog_post, :author
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # In a view
|
23
|
+
# presenter = p(:post, @some_post, @some_user)
|
24
|
+
# presenter.blog_post # == @some_post
|
25
|
+
# presenter.author # == @some_user
|
26
|
+
#
|
27
|
+
def presents(*objects)
|
28
|
+
objects.unshift :view
|
29
|
+
attr_reader *objects
|
30
|
+
|
31
|
+
param_list = objects.join(', ')
|
32
|
+
ivar_list = objects.map { |o| "@#{o}" }.join(', ')
|
33
|
+
|
34
|
+
class_eval <<-RUBY
|
35
|
+
def initialize(#{param_list}) # def initialize(view, foo)
|
36
|
+
#{ivar_list} = #{param_list} # @view, @foo = view, foo
|
37
|
+
end # end
|
38
|
+
RUBY
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @private (used by Keynote::Cache to keep the view context up-to-date)
|
43
|
+
attr_writer :view
|
44
|
+
|
45
|
+
# Create a presenter. The default constructor takes one parameter, but
|
46
|
+
# calling `presents` replaces it with a generated constructor.
|
47
|
+
# @param [ActionView::Base] view_context
|
48
|
+
# @see Keynote::Presenter.presents
|
49
|
+
def initialize(view_context)
|
50
|
+
@view = view_context
|
51
|
+
end
|
52
|
+
|
53
|
+
# Instantiate another presenter.
|
54
|
+
# @see Keynote.present
|
55
|
+
def present(*objects)
|
56
|
+
Keynote.present(@view, *objects)
|
57
|
+
end
|
58
|
+
alias p present
|
59
|
+
|
60
|
+
# @private
|
61
|
+
def respond_to_missing?(method_name, include_private = true)
|
62
|
+
@view.respond_to?(method_name, true)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Presenters proxy unknown method calls to the view context, allowing you
|
66
|
+
# to call `h`, `link_to`, and anything else defined in a helper module.
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
# def title_link
|
70
|
+
# link_to blog_post_url(blog_post) do
|
71
|
+
# "#{h author.name} — #{h blog_post.title}".html_safe
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
def method_missing(method_name, *args, &block)
|
75
|
+
if @view.respond_to?(method_name, true)
|
76
|
+
@view.send(method_name, *args, &block)
|
77
|
+
else
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# We have to explicitly proxy `#capture` because ActiveSupport creates a
|
85
|
+
# `Kernel#capture` method.
|
86
|
+
def capture(*args, &block)
|
87
|
+
@view.capture(*args, &block)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|