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.

@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ log
19
+ gemfiles
@@ -0,0 +1,4 @@
1
+ --markup markdown
2
+ --no-private
3
+ --exclude lib/generators
4
+ --exclude keynote/testing
@@ -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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in keynote.gemspec
4
+ gemspec
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.
@@ -0,0 +1,8 @@
1
+ ## Keynote
2
+
3
+ Presenters for Rails.
4
+
5
+ ### TODO
6
+
7
+ * Generators.
8
+ * Documentation.
@@ -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]
@@ -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
@@ -0,0 +1,11 @@
1
+ require "minitest_helper"
2
+
3
+ describe <%= class_name %>Presenter do
4
+ before do
5
+ @presenter = <%= class_name %>Presenter.new(<%= view_and_target_list %>)
6
+ end
7
+
8
+ # it "must be a real test" do
9
+ # flunk "Need real tests"
10
+ # end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'minitest_helper'
2
+
3
+ class <%= class_name %>PresenterTest < Keynote::MiniTest::TestCase
4
+ setup do
5
+ @presenter = <%= class_name %>Presenter.new(<%= view_and_target_list %>)
6
+ end
7
+
8
+ # test "the truth" do
9
+ # assert true
10
+ # end
11
+ end
@@ -0,0 +1,7 @@
1
+ <% module_namespacing do -%>
2
+ class <%= class_name %>Presenter < Keynote::Presenter
3
+ <% if targets.any? -%>
4
+ presents <%= target_list %>
5
+ <% end -%>
6
+ end
7
+ <% end -%>
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe <%= class_name %>Presenter do
4
+ subject { <%= class_name %>Presenter.new(<%= view_and_target_list %>) }
5
+ end
@@ -0,0 +1,11 @@
1
+ require 'test_helper'
2
+
3
+ class <%= class_name %>PresenterTest < Keynote::TestCase
4
+ setup do
5
+ @presenter = <%= class_name %>Presenter.new(<%= view_and_target_list %>)
6
+ end
7
+
8
+ # test "the truth" do
9
+ # assert true
10
+ # end
11
+ end
@@ -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} &mdash; #{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