assaf-presenter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +0 -0
- data/MIT-LICENSE +0 -0
- data/README +0 -0
- data/Rakefile +23 -0
- data/lib/presenter.rb +9 -0
- data/lib/presenter/base.rb +115 -0
- data/lib/presenter/method.rb +46 -0
- data/lib/presenter/rspec.rb +33 -0
- data/presenter.gemspec +22 -0
- data/rails/init.rb +9 -0
- data/spec/helper.rb +15 -0
- data/spec/presenter_spec.rb +73 -0
- data/spec/presenting_method_spec.rb +111 -0
- metadata +82 -0
data/CHANGELOG
ADDED
File without changes
|
data/MIT-LICENSE
ADDED
File without changes
|
data/README
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rake/gempackagetask'
|
2
|
+
|
3
|
+
|
4
|
+
spec = Gem::Specification.load(File.join(File.dirname(__FILE__), 'presenter.gemspec'))
|
5
|
+
|
6
|
+
task 'setup' do
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
package = Rake::GemPackageTask.new(spec) do |pkg|
|
11
|
+
pkg.need_tar = true
|
12
|
+
pkg.need_zip = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Install the package locally"
|
16
|
+
task 'install'=>['setup', 'package'] do |task|
|
17
|
+
system 'sudo', 'gem', 'install', "pkg/#{spec.name}-#{spec.version}.gem"
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Uninstall previously installed packaged"
|
21
|
+
task 'uninstall' do |task|
|
22
|
+
system 'sudo', 'gem', 'uninstall', spec.name, '-v', spec.version.to_s
|
23
|
+
end
|
data/lib/presenter.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
module Presenter
|
2
|
+
class Base
|
3
|
+
|
4
|
+
class << self
|
5
|
+
# Item name, derived from the class name (e.g. TaskPresenter becomes 'task').
|
6
|
+
# Used for the XML document element name, also name of the accessor method used
|
7
|
+
# to read the presented object.
|
8
|
+
attr_reader :item_name
|
9
|
+
|
10
|
+
# Array name, derived from the class name (e.g. TaskPresenter becomes 'tasks').
|
11
|
+
# Used for the XML document element name, also name of the accessor method used
|
12
|
+
# to read the presented array of objects.
|
13
|
+
attr_reader :array_name
|
14
|
+
|
15
|
+
def inherited(klass)
|
16
|
+
name = klass.name[/(.*?)(Presenter)?$/, 1].underscore
|
17
|
+
klass.instance_variable_set(:@item_name, name)
|
18
|
+
klass.class_eval "def #{klass.item_name} ; @item ; end"
|
19
|
+
klass.instance_variable_set(:@array_name, name.pluralize)
|
20
|
+
klass.class_eval "def #{klass.array_name} ; @array ; end"
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
# Creates new presenter using the given controller and item/array.
|
26
|
+
def initialize(controller, value)
|
27
|
+
@controller = controller
|
28
|
+
if value.is_a?(Array)
|
29
|
+
@array = value
|
30
|
+
else
|
31
|
+
@item = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
include PresentingMethod
|
36
|
+
|
37
|
+
# Controller associated with this presenter. Used primarily to create URLs
|
38
|
+
# and access various helper methods.
|
39
|
+
attr_reader :controller
|
40
|
+
|
41
|
+
# Item being presented. This accessor has a value when presenting a single
|
42
|
+
# object, for arrays see #array. Can also be accessed from named attribute,
|
43
|
+
# e.g. the #task method on TaskPresenter.
|
44
|
+
attr_reader :item
|
45
|
+
|
46
|
+
# Array being presented. This accessor has a value when presenting an array
|
47
|
+
# of objects, for single item see #item. Can also be accessed from named attribute,
|
48
|
+
# e.g. the #tasks method on TaskPresenter.
|
49
|
+
attr_reader :array
|
50
|
+
|
51
|
+
# Converts to JSON document, returns a String. The default implementation
|
52
|
+
# uses #map to convert the instance or each member of the array, and calls
|
53
|
+
# #to_json on the result.
|
54
|
+
#
|
55
|
+
# For example:
|
56
|
+
# render :json=>presenting(@item)
|
57
|
+
def to_json(options = {})
|
58
|
+
@array ? @array.map { |i| hash_for(i) }.to_json(options) :
|
59
|
+
hash_for(@item).to_json(options)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Converts to XML document, returns a String. The default implementation
|
63
|
+
# uses #map to convert the instance or each member of the array, and calls
|
64
|
+
# #to_xml on the result.
|
65
|
+
#
|
66
|
+
# For example:
|
67
|
+
# render :xml=>presenting(@items)
|
68
|
+
def to_xml(options = {})
|
69
|
+
@array ? @array.map { |i| hash_for(i) }.to_xml({:root=>self.class.array_name}.merge(options)) :
|
70
|
+
hash_for(@item).to_xml({:root=>self.class.item_name}.merge(options))
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
|
75
|
+
# Shortcut for CGI::escapeHTML.
|
76
|
+
def h(text)
|
77
|
+
CGI::escapeHTML(text)
|
78
|
+
end
|
79
|
+
|
80
|
+
include ActionController::UrlWriter
|
81
|
+
# TODO: do we need this?
|
82
|
+
# default_url_options[:host] = 'test.host' if defined?(RAILS_ENV) && RAILS_ENV == 'test'
|
83
|
+
|
84
|
+
def url_for_with_controller(*args)
|
85
|
+
if controller
|
86
|
+
controller.url_for(*args)
|
87
|
+
else
|
88
|
+
url_for_without_controller(*args)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
alias_method_chain :url_for, :controller
|
92
|
+
|
93
|
+
# Request to which this controller is responding.
|
94
|
+
def request
|
95
|
+
controller && controller.request
|
96
|
+
end
|
97
|
+
|
98
|
+
# Converts object into a hash. JSON, XML and other output formats use this
|
99
|
+
# before serializing the result into the respective content type.
|
100
|
+
#
|
101
|
+
# Override to do all sorts of fancy tricks. The defult implementation calls
|
102
|
+
# the attributes methods (works on ActiveRecord models), lacking that the to_hash
|
103
|
+
# method, and if neither is found return an empty hash.
|
104
|
+
def hash_for(object)
|
105
|
+
object.respond_to?(:attributes) && object.attributes ||
|
106
|
+
object.respond_to?(:to_hash) && object.to_hash || {}
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns an ID using the host name, object class and object identifier.
|
110
|
+
def id_for(object)
|
111
|
+
"tag:#{request.host}:#{object.class.underscore}/#{object.id}"
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Presenter
|
2
|
+
module PresentingMethod
|
3
|
+
|
4
|
+
# Return a new presenter. This method can be called in several configurations to
|
5
|
+
# present both an instance and an array:
|
6
|
+
#
|
7
|
+
# You can call this method in several configurations to present both singular objects
|
8
|
+
# and arrays. Using an explicit type followed by value:
|
9
|
+
# presenting(class, object_or_array)
|
10
|
+
# The first argument specifies the expected type of the object/array that follows,
|
11
|
+
# and that argument is used to find the presenter class. For example, <tt>presenting(Foo,[])<tt>
|
12
|
+
# uses <tt>FooPresenter</tt>.
|
13
|
+
# presenting(symbol, object_or_array)
|
14
|
+
# This method is similar but replaces explicit class with symbol, so <tt>foo:<tt> instead
|
15
|
+
# of <tt>Foo</tt>.
|
16
|
+
# presenting(object)
|
17
|
+
# The third form takes a single argument and finds the presenter class based on the
|
18
|
+
# argument type, for example, <tt>presenting(Foo.new)</tt> will use the presenter class
|
19
|
+
# <tt>FooPresenter</tt>.
|
20
|
+
# presenting(array)
|
21
|
+
# Since arrays may be empty it is not always possible to use the array content to figure
|
22
|
+
# out the presenter class. Either specify it explicitly as the first argument (see above),
|
23
|
+
# or let the presenting method figure it out from the controller name, for example,
|
24
|
+
# inferring <tt>FooPresenter</tt> from <tt>FooController</tt>.
|
25
|
+
def presenting(*args)
|
26
|
+
controller = ActionController::Base === self ? self : self.controller
|
27
|
+
case args.first
|
28
|
+
when Class # Presented class followed by instance/array
|
29
|
+
name = args.shift.to_s
|
30
|
+
value = args.shift
|
31
|
+
when Symbol # Presenter name (symbol) followed by instance/array
|
32
|
+
name = args.shift.to_s.capitalize
|
33
|
+
value = args.shift
|
34
|
+
when Array # Array of values, pick presenter from item type
|
35
|
+
value = args.shift
|
36
|
+
name = controller.class.to_s.gsub(/Controller$/, '').classify
|
37
|
+
else # Presenter for single instance.
|
38
|
+
value = args.shift
|
39
|
+
name = value.class.name
|
40
|
+
end
|
41
|
+
raise ArgumentError, "Unexpected arguments #{args.inspect}" unless args.empty?
|
42
|
+
Class.const_get("#{name}Presenter").new(controller, value)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Spec
|
2
|
+
module Rails
|
3
|
+
module Matchers
|
4
|
+
|
5
|
+
class PresentMatcher
|
6
|
+
def initialize(presenter, format)
|
7
|
+
@presenter = presenter
|
8
|
+
@format = format
|
9
|
+
end
|
10
|
+
|
11
|
+
def matches?(response)
|
12
|
+
@expect = response.body
|
13
|
+
@actual = @presenter.send("to_#{@format.to_sym}")
|
14
|
+
@actual == @expect
|
15
|
+
end
|
16
|
+
|
17
|
+
def failure_message
|
18
|
+
"Expected #{@expect.inspect}, found #{@actual.inspect}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def negative_failure_message
|
22
|
+
"Found unexpected #{@actual.inspect}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
include Presenter::PresentingMethod
|
27
|
+
|
28
|
+
def present(*args)
|
29
|
+
PresentMatcher.new(presenting(*args), request.format)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/presenter.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = 'presenter'
|
3
|
+
spec.version = '0.1.0'
|
4
|
+
spec.author = 'Rails Presenter'
|
5
|
+
spec.email = 'assaf@labnotes.org'
|
6
|
+
spec.homepage = "http://github.com/assaf/#{spec.name}"
|
7
|
+
spec.summary = "Add later ..." # TODO
|
8
|
+
|
9
|
+
spec.files = Dir['lib/**/*', 'rails/**/*', 'README', 'CHANGELOG', 'MIT-LICENSE',
|
10
|
+
'*.gemspec', 'Rakefile', 'spec/**/*', 'doc/**/*']
|
11
|
+
spec.require_paths = 'lib'
|
12
|
+
|
13
|
+
spec.has_rdoc = true
|
14
|
+
spec.extra_rdoc_files = 'README', 'CHANGELOG', 'MIT-LICENSE'
|
15
|
+
spec.rdoc_options = '--title', spec.name,
|
16
|
+
'--main', 'README', '--line-numbers', '--inline-source',
|
17
|
+
'--webcvs', "#{spec.homepage}/tree/master"
|
18
|
+
spec.rubyforge_project = spec.name
|
19
|
+
|
20
|
+
# Tested against these dependencies.
|
21
|
+
spec.add_dependency 'rails'
|
22
|
+
end
|
data/rails/init.rb
ADDED
data/spec/helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
ENV['RAILS_ENV'] ||= 'test'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
|
3
|
+
require 'spec/rails'
|
4
|
+
|
5
|
+
# Object being presented.
|
6
|
+
class Foo
|
7
|
+
end
|
8
|
+
|
9
|
+
# Presenter for Foo.
|
10
|
+
class FooPresenter < Presenter::Base
|
11
|
+
end
|
12
|
+
|
13
|
+
# Test controller, using similar name so we can deduct presenter from controller.
|
14
|
+
class FooController < ApplicationController
|
15
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
|
4
|
+
describe 'Presenter', :type=>:controller do
|
5
|
+
controller_name :foo
|
6
|
+
|
7
|
+
it 'should derive item name from class name' do
|
8
|
+
FooPresenter.item_name.should == 'foo'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should derive array name from element name' do
|
12
|
+
FooPresenter.array_name.should == 'foos'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should have accessor for controller' do
|
16
|
+
presenting(Foo.new).controller.should == controller
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '(instance)' do
|
20
|
+
before { @presenter = presenting(@item = Foo.new) }
|
21
|
+
|
22
|
+
it 'should provide object from #item accessor' do
|
23
|
+
@presenter.item.should == @item
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should provide object from (item) accessor' do
|
27
|
+
@presenter.foo.should == @item
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should provide nothing from #array accessor' do
|
31
|
+
@presenter.array.should be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should use hash_for(instance) for to_json' do
|
35
|
+
@presenter.should_receive(:hash_for).with(@item).and_return('bar'=>'beer')
|
36
|
+
@presenter.to_json.should == %{{"bar": "beer"}}
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should use hash_for(instance) for to_xml with model name' do
|
40
|
+
@presenter.should_receive(:hash_for).with(@item).and_return('bar'=>'beer')
|
41
|
+
@presenter.to_xml(:indent=>0).should == %{<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo><bar>beer</bar></foo>}
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '(array)' do
|
47
|
+
before { @presenter = presenting(@array = [Foo.new, Foo.new]) }
|
48
|
+
|
49
|
+
it 'should provide array from #array accessor' do
|
50
|
+
@presenter.array.should == @array
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should provide array from (array) accessor' do
|
54
|
+
@presenter.foos.should == @array
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should provide nothing from #item accessor' do
|
58
|
+
@presenter.item.should be_nil
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should use array.map(hash_for) for to_json' do
|
62
|
+
@presenter.should_receive(:hash_for).twice.and_return({'bar'=>1}, {'bar'=>2})
|
63
|
+
@presenter.to_json.should == %{[{"bar": 1}, {"bar": 2}]}
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should use hash_for(instance) for to_xml with model name' do
|
67
|
+
@presenter.should_receive(:hash_for).twice.and_return({'bar'=>1}, {'bar'=>2})
|
68
|
+
@presenter.to_xml(:indent=>0, :types=>false).should == %{<?xml version=\"1.0\" encoding=\"UTF-8\"?><foos type="array"><foo><bar type="integer">1</bar></foo><foo><bar type="integer">2</bar></foo></foos>}
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
|
4
|
+
describe 'presenting', :type=>:controller do
|
5
|
+
controller_name :foo
|
6
|
+
|
7
|
+
it 'should be available on controller' do
|
8
|
+
ApplicationController.instance_method(:presenting).to_s.should =~ /presenting/
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should be available on view' do
|
12
|
+
ActionView::Base.instance_method(:presenting).to_s.should =~ /presenting/
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should be available on presenter' do
|
16
|
+
FooPresenter.instance_method(:presenting).to_s.should =~ /presenting/
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '(class,instance)' do
|
20
|
+
it 'should guess presenter type from class' do
|
21
|
+
presenting(Foo, 1).should be_kind_of(FooPresenter)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should return presenter for instance' do
|
25
|
+
presenting(Foo, 1).item.should == 1
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '(class,array)' do
|
30
|
+
it 'should guess presenter type from class' do
|
31
|
+
presenting(Foo, [1, 2]).should be_kind_of(FooPresenter)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should return presenter for array' do
|
35
|
+
presenting(Foo, [1, 2]).array.should == [1, 2]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '(symbol,instance)' do
|
40
|
+
it 'should guess presenter type from symbol' do
|
41
|
+
presenting(:foo, 1).should be_kind_of(FooPresenter)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should return presenter for instance' do
|
45
|
+
presenting(:foo, 1).item.should == 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '(symbol,array)' do
|
50
|
+
it 'should guess presenter type from class' do
|
51
|
+
presenting(:foo, [1, 2]).should be_kind_of(FooPresenter)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should return presenter for array' do
|
55
|
+
presenting(:foo, [1, 2]).array.should == [1, 2]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '(instance)' do
|
60
|
+
before { @instance = Foo.new }
|
61
|
+
|
62
|
+
it 'should guess presenter type from value' do
|
63
|
+
presenting(@instance).should be_kind_of(FooPresenter)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should return presenter for instance' do
|
67
|
+
presenting(@instance).item.should == @instance
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should fail if value is nil' do
|
71
|
+
lambda { presenting(nil) }.should raise_error(NameError)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '(array)' do
|
76
|
+
before { @array = [Foo.new, Foo.new] }
|
77
|
+
|
78
|
+
it 'should guess presenter type from controller' do
|
79
|
+
presenting(@array).should be_kind_of(FooPresenter)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should return presenter for array' do
|
83
|
+
presenting(@array).array.should == @array
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '(empty array)' do
|
88
|
+
before { @empty = [] }
|
89
|
+
|
90
|
+
it 'should guess presenter type from controller' do
|
91
|
+
presenting(@empty).should be_kind_of(FooPresenter)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should return presenter for empty array' do
|
95
|
+
presenting(@empty).array.should be_empty
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should use context as controller if context is controller' do
|
100
|
+
controller.presenting([]).controller.should == controller
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should borrow controller from context if context is not controller' do
|
104
|
+
presenting([]).controller.should == controller
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should fail if passed too many arguments' do
|
108
|
+
lambda { presenting(:foo, [], :bar) }.should raise_error(ArgumentError)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: assaf-presenter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rails Presenter
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-08-13 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rails
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
description:
|
25
|
+
email: assaf@labnotes.org
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files:
|
31
|
+
- README
|
32
|
+
- CHANGELOG
|
33
|
+
- MIT-LICENSE
|
34
|
+
files:
|
35
|
+
- lib/presenter.rb
|
36
|
+
- lib/presenter
|
37
|
+
- lib/presenter/rspec.rb
|
38
|
+
- lib/presenter/base.rb
|
39
|
+
- lib/presenter/method.rb
|
40
|
+
- rails/init.rb
|
41
|
+
- README
|
42
|
+
- CHANGELOG
|
43
|
+
- MIT-LICENSE
|
44
|
+
- presenter.gemspec
|
45
|
+
- Rakefile
|
46
|
+
- spec/presenting_method_spec.rb
|
47
|
+
- spec/presenter_spec.rb
|
48
|
+
- spec/helper.rb
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: http://github.com/assaf/presenter
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options:
|
53
|
+
- --title
|
54
|
+
- presenter
|
55
|
+
- --main
|
56
|
+
- README
|
57
|
+
- --line-numbers
|
58
|
+
- --inline-source
|
59
|
+
- --webcvs
|
60
|
+
- http://github.com/assaf/presenter/tree/master
|
61
|
+
require_paths: lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project: presenter
|
77
|
+
rubygems_version: 1.2.0
|
78
|
+
signing_key:
|
79
|
+
specification_version: 2
|
80
|
+
summary: Add later ...
|
81
|
+
test_files: []
|
82
|
+
|