agnostic_presenters 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Yeasty Mobs
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # AgnosticPresenters
2
+
3
+ AgnosticPresenters is a gem that allow you to have presenters for your objects with a minimal syntax (because we are lazy).
4
+
5
+ ## TODO
6
+
7
+ * Rename Base and Helper.
8
+ * Do a proper tweak of Proxen
9
+
10
+ ## Usage
11
+
12
+ <pre><code>require "rubygems"
13
+ gem "yeastymobs-agnostic_presenters"
14
+ require "agnostic_presenters"
15
+
16
+ Class.send :include, AgnosticPresenters::Helper
17
+
18
+ class MyModel
19
+
20
+ # ...
21
+
22
+ presenters do
23
+
24
+ def myattribute
25
+ "#{object.fancy} with a presenter"
26
+ end
27
+
28
+ end
29
+ end
30
+
31
+ @model = MyModel.new(:myattribute => "fancy")
32
+ @model.myattribute # => fancy
33
+ @model.myattribute! # => fancy with a presenter</code></pre>
34
+
35
+ And you can share helpers between presenters with just a line:
36
+
37
+ <pre><code>module Helpers
38
+ def escape(string)
39
+ Rack::Utils.escape(string)
40
+ end
41
+ end
42
+
43
+ AgnosticPresenters::Base.send :include, Helpers</code></pre>
44
+
45
+ ## Usage with Rails
46
+
47
+ <pre><code>config.gem "yeastymobs-agnostic_presenters", :lib => "agnostic_presenters/activerecord", :source => "http://gems.github.com"</code></pre>
48
+
49
+ And now you have presenters in your models which include ActionView helpers:
50
+
51
+ <pre><code>class Article < ActiveRecord::Base
52
+ presenters do
53
+
54
+ def title
55
+ h object.title
56
+ end
57
+
58
+ def slug
59
+ object.title.parameterize
60
+ end
61
+
62
+ end
63
+ end</code></pre>
64
+
65
+ If you use another ORM, just change "activerecord".
66
+
67
+ ## Supported ORMs
68
+
69
+ * ActiveRecord "agnostic_presenters/activerecord"
70
+ * MongoMapper "agnostic_presenters/mongomapper"
71
+
72
+ ## Credits
73
+
74
+ * Nicolas Mérouze
75
+ * A part of code is taken from active-record-presenters plugin
76
+ * The proxy part is from nakajima-proxen gem
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'spec/rake/spectask'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = "agnostic_presenters"
9
+ gem.summary = %Q{Agnostic Presenters}
10
+ gem.email = "dev@yeastymobs.com"
11
+ gem.homepage = "http://github.com/yeastymobs/agnostic_presenters"
12
+ gem.authors = ["Nicolas Mérouze"]
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
16
+ end
17
+
18
+ desc 'Default: run specs.'
19
+ task :default => :spec
20
+
21
+ desc 'Run all the specs for the machinist plugin.'
22
+ Spec::Rake::SpecTask.new do |t|
23
+ t.spec_files = FileList['spec/**/*_spec.rb']
24
+ t.rcov = false
25
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,54 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{agnostic_presenters}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Nicolas M\303\251rouze"]
12
+ s.date = %q{2010-06-11}
13
+ s.email = %q{dev@yeastymobs.com}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "LICENSE",
21
+ "README.md",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "agnostic_presenters.gemspec",
25
+ "lib/agnostic_presenters.rb",
26
+ "lib/agnostic_presenters/activerecord.rb",
27
+ "lib/agnostic_presenters/mongomapper.rb",
28
+ "lib/base.rb",
29
+ "lib/proxen.rb",
30
+ "lib/rails-init.rb",
31
+ "spec/agnostic_presenters_spec.rb",
32
+ "spec/proxen_spec.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/yeastymobs/agnostic_presenters}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.7}
38
+ s.summary = %q{Agnostic Presenters}
39
+ s.test_files = [
40
+ "spec/agnostic_presenters_spec.rb",
41
+ "spec/proxen_spec.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ else
50
+ end
51
+ else
52
+ end
53
+ end
54
+
@@ -0,0 +1,2 @@
1
+ require 'base'
2
+ require 'rails-init'
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), "../agnostic_presenters")
2
+ ActiveRecord::Base.send :extend, AgnosticPresenters::Helper if defined?(ActiveRecord)
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), "../agnostic_presenters")
2
+ MongoMapper::Document.append_extensions(AgnosticPresenters::Helper) if defined?(MongoMapper)
data/lib/base.rb ADDED
@@ -0,0 +1,38 @@
1
+ require "proxen"
2
+
3
+ module AgnosticPresenters
4
+ class NoPresenter < StandardError; end
5
+
6
+ class Base
7
+ attr_reader :object
8
+
9
+ def initialize(object)
10
+ @object = object
11
+ end
12
+ end
13
+
14
+ module Helper
15
+
16
+ def presenters(&block)
17
+ klass = Class.new(::AgnosticPresenters::Base)
18
+ klass.class_eval(&block)
19
+ const_set(:Presenters, klass)
20
+
21
+ self.send :include, Proxen
22
+ self.class_eval do
23
+ Proxen::Proxy.add(self, :_presenters, :if => /!$/)
24
+
25
+ def method_missing(sym, *args, &block)
26
+ Proxen::Proxy.handle(self, sym, *args, &block) || super
27
+ end
28
+
29
+ def _presenters
30
+ @_presenters ||= self.class::Presenters.new(self)
31
+ rescue NameError
32
+ raise ::AgnosticPresenters::NoPresenter, "No presenter has been found for #{self.class.name}."
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+ end
data/lib/proxen.rb ADDED
@@ -0,0 +1,106 @@
1
+ module Proxen
2
+ class Proxy
3
+ class << self
4
+ def add(klass, *args)
5
+ store[klass] = new(klass, *args)
6
+ end
7
+
8
+ def handle(instance, sym, *args, &block)
9
+ klass = Object.instance_method(:class).bind(instance).call
10
+ if proxy = store[klass]
11
+ proxy.handle(instance, sym, *args, &block)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def store
18
+ @store ||= {}
19
+ end
20
+ end
21
+
22
+ def initialize(klass, *args)
23
+ @klass = klass
24
+ @options = args.last.is_a?(Hash) ? args.pop : {}
25
+ @targets = Array(args).flatten
26
+
27
+ blankify! if @options[:blank_slate]
28
+ end
29
+
30
+ def handle(instance, sym, *args, &block)
31
+ if target = target_for(instance, sym)
32
+ sym = sym.to_s[0..-2] # Delete !
33
+ if @options[:compile]
34
+ compile(target, sym)
35
+ instance.__send__(sym, *args, &block)
36
+ else
37
+ instance.__send__(target).__send__(sym, *args, &block)
38
+ end
39
+ end
40
+ end
41
+
42
+ def blankify!
43
+ @klass.class_eval do
44
+ instance_methods.each do |sym|
45
+ undef_method(sym) unless sym.to_s =~ /__/
46
+ end
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def compile(receiver, sym)
53
+ @klass.class_eval(<<-END, __FILE__, __LINE__)
54
+ def #{sym}(*args, &block)
55
+ #{receiver}.send(#{sym.inspect}, *args, &block)
56
+ end
57
+ END
58
+ end
59
+
60
+ def proxying?(instance, sym)
61
+ case @options[:if] || @options[:unless]
62
+ when Proc then calls?(sym)
63
+ when Regexp then match?(sym)
64
+ when Symbol then sends?(instance, sym)
65
+ else true
66
+ end
67
+ end
68
+
69
+ def calls?(sym)
70
+ case
71
+ when fn = @options[:if] then fn.call(sym)
72
+ when fn = @options[:unless] then not fn.call(sym)
73
+ end
74
+ end
75
+
76
+ def sends?(instance, sym)
77
+ case
78
+ when cond = @options[:if] then instance.__send__(cond, sym)
79
+ when cond = @options[:unless] then not instance.__send__(cond, sym)
80
+ end
81
+ end
82
+
83
+ def match?(sym)
84
+ case
85
+ when regex = @options[:if] then sym.to_s =~ regex
86
+ when regex = @options[:unless] then not sym.to_s =~ regex
87
+ end
88
+ end
89
+
90
+ def target_for(instance, sym)
91
+ return nil unless proxying?(instance, sym)
92
+ sym = sym.to_s[0..-2] # Delete !
93
+ @targets.detect { |t| instance.__send__(t).respond_to?(sym) }
94
+ end
95
+ end
96
+
97
+ def proxy_to(*targets)
98
+ Proxen::Proxy.add(self, *targets)
99
+
100
+ class_eval(<<-END, __FILE__, __LINE__)
101
+ def method_missing(sym, *args, &block)
102
+ Proxen::Proxy.handle(self, sym, *args, &block) || super
103
+ end
104
+ END
105
+ end
106
+ end
data/lib/rails-init.rb ADDED
@@ -0,0 +1,16 @@
1
+ File.join(File.dirname(__FILE__), "../lib/agnostic_presenters")
2
+ require 'base'
3
+
4
+ if defined?(Rails)
5
+ AgnosticPresenters::Base.send :include, ActionView::Helpers
6
+
7
+ class << ActionController::Base
8
+ def add_template_helper_with_presenters(helper_module, *args, &block)
9
+ # Hijacking any helper added to controllers so that our presenters can acces 'em.
10
+ AgnosticPresenters::Base.instance_eval { include helper_module }
11
+ add_template_helper_without_presenters(helper_module, *args, &block)
12
+ end
13
+
14
+ alias_method_chain :add_template_helper, :presenters
15
+ end
16
+ end
@@ -0,0 +1,51 @@
1
+ require "rubygems"
2
+ require "spec"
3
+ require "rack"
4
+
5
+ $:.push File.join(File.dirname(__FILE__), "../lib")
6
+ require "agnostic_presenters"
7
+
8
+ # Helpers
9
+
10
+ module Helpers
11
+
12
+ def escape(string)
13
+ Rack::Utils.escape(string)
14
+ end
15
+
16
+ end
17
+
18
+ AgnosticPresenters::Base.send :include, Helpers
19
+
20
+ # Model
21
+
22
+ Class.send :include, AgnosticPresenters::Helper
23
+
24
+ class Article
25
+ attr_accessor :title, :body
26
+
27
+ def initialize(title, body)
28
+ @title, @body = title, body
29
+ end
30
+
31
+ presenters do
32
+
33
+ def title
34
+ escape(object.title)
35
+ end
36
+
37
+ end
38
+ end
39
+
40
+ describe Article do
41
+
42
+ before(:all) do
43
+ @article = Article.new("[Agnostic Presenters]", "New presenter plugin.")
44
+ end
45
+
46
+ it "should present title" do
47
+ @article.title!.should_not == @article.title
48
+ @article.title!.should == "%5BAgnostic+Presenters%5D"
49
+ end
50
+
51
+ end
@@ -0,0 +1,211 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'rr'
4
+
5
+ require File.dirname(__FILE__) + '/../lib/proxen'
6
+ Class.send :include, Proxen
7
+
8
+ Spec::Runner.configure { |c| c.mock_with(:rr) }
9
+
10
+ require 'ostruct'
11
+
12
+ describe Proxen do
13
+ it "proxies method_missing to method" do
14
+ @klass = Class.new do
15
+ proxy_to :foo
16
+
17
+ def foo
18
+ Class.new { def bar; :proxied end }.new
19
+ end
20
+ end
21
+
22
+ @klass.new.bar.should == :proxied
23
+ end
24
+
25
+ it "respects other method missings" do
26
+ @klass = Class.new do
27
+ proxy_to :foo
28
+
29
+ def foo
30
+ Class.new { def bar; :proxied end }.new
31
+ end
32
+
33
+ def method_missing(sym, *args, &block)
34
+ :not_proxied
35
+ end
36
+ end
37
+
38
+ @klass.new.bar.should == :not_proxied
39
+ end
40
+
41
+ it "can generate blank slates" do
42
+ @klass = Class.new do
43
+ proxy_to :foo, :blank_slate => true
44
+
45
+ def foo
46
+ Class.new { def inspect; :proxied end }.new
47
+ end
48
+ end
49
+
50
+ @klass.new.inspect.should == :proxied
51
+ end
52
+
53
+ it "accepts :if method" do
54
+ @klass = Class.new do
55
+ proxy_to :foo, :if => :should?
56
+
57
+ def foo
58
+ Class.new {
59
+ def fizz; :proxied end
60
+ def buzz; :proxied end
61
+ }.new
62
+ end
63
+
64
+ def should?(sym)
65
+ sym != :buzz
66
+ end
67
+ end
68
+
69
+ @klass.new.fizz.should == :proxied
70
+
71
+ proc {
72
+ @klass.new.buzz
73
+ }.should raise_error(NoMethodError)
74
+ end
75
+
76
+ it "accepts :unless method" do
77
+ @klass = Class.new do
78
+ proxy_to :foo, :unless => :should_not?
79
+
80
+ def foo
81
+ Class.new {
82
+ def fizz; :proxied end
83
+ def buzz; :proxied end
84
+ }.new
85
+ end
86
+
87
+ def should_not?(sym)
88
+ sym == :buzz
89
+ end
90
+ end
91
+
92
+ @klass.new.fizz.should == :proxied
93
+
94
+ proc {
95
+ @klass.new.buzz
96
+ }.should raise_error(NoMethodError)
97
+ end
98
+
99
+ it "accepts :if proc" do
100
+ @klass = Class.new do
101
+ proxy_to :foo, :if => proc { |sym| sym == :fizz }
102
+
103
+ def foo
104
+ Class.new {
105
+ def fizz; :proxied end
106
+ def buzz; :proxied end
107
+ }.new
108
+ end
109
+ end
110
+
111
+ @klass.new.fizz.should == :proxied
112
+
113
+ proc {
114
+ @klass.new.buzz
115
+ }.should raise_error(NoMethodError)
116
+ end
117
+
118
+ it "accepts :unless proc" do
119
+ @klass = Class.new do
120
+ proxy_to :foo, :unless => proc { |sym| sym == :buzz }
121
+
122
+ def foo
123
+ Class.new {
124
+ def fizz; :proxied end
125
+ def buzz; :proxied end
126
+ }.new
127
+ end
128
+ end
129
+
130
+ @klass.new.fizz.should == :proxied
131
+
132
+ proc {
133
+ @klass.new.buzz
134
+ }.should raise_error(NoMethodError)
135
+ end
136
+
137
+ it "accepts :if regexen" do
138
+ @klass = Class.new do
139
+ proxy_to :foo, :if => /zz/
140
+
141
+ def foo
142
+ Class.new {
143
+ def baz; :err end
144
+ def fizz; :proxied end
145
+ def buzz; :proxied end
146
+ }.new
147
+ end
148
+ end
149
+
150
+ @klass.new.fizz.should == :proxied
151
+ @klass.new.buzz.should == :proxied
152
+
153
+ proc {
154
+ @klass.new.baz.should == :proxied
155
+ }.should raise_error(NoMethodError)
156
+ end
157
+
158
+ it "accepts :unless regexen" do
159
+ @klass = Class.new do
160
+ proxy_to :foo, :unless => /zz/
161
+
162
+ def foo
163
+ Class.new {
164
+ def baz; :proxied end
165
+ def fizz; :err end
166
+ def buzz; :err end
167
+ }.new
168
+ end
169
+ end
170
+
171
+ @klass.new.baz.should == :proxied
172
+
173
+ proc {
174
+ @klass.new.fizz
175
+ @klass.new.buzz
176
+ }.should raise_error(NoMethodError)
177
+ end
178
+
179
+ it "proxies to multiple" do
180
+ @klass = Class.new do
181
+ proxy_to :foo, :bar
182
+
183
+ def foo
184
+ Class.new { def fizz; :proxied end }.new
185
+ end
186
+
187
+ def bar
188
+ Class.new { def buzz; :proxied end }.new
189
+ end
190
+ end
191
+
192
+ @klass.new.fizz.should == :proxied
193
+ @klass.new.buzz.should == :proxied
194
+ end
195
+
196
+ it "defines methods so that they don't require method missing" do
197
+ @compiled = Class.new {
198
+ proxy_to :foo, :blank_slate => true, :compile => true
199
+
200
+ def foo
201
+ :COMPILED!
202
+ end
203
+ }.new
204
+
205
+ @compiled.to_s
206
+
207
+ mock.proxy(Proxen::Proxy).handle(anything).never
208
+
209
+ @compiled.to_s.should == 'COMPILED!'
210
+ end
211
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agnostic_presenters
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - "Nicolas M\xC3\xA9rouze"
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-06-11 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description:
23
+ email: dev@yeastymobs.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - LICENSE
30
+ - README.md
31
+ files:
32
+ - .gitignore
33
+ - LICENSE
34
+ - README.md
35
+ - Rakefile
36
+ - VERSION
37
+ - agnostic_presenters.gemspec
38
+ - lib/agnostic_presenters.rb
39
+ - lib/agnostic_presenters/activerecord.rb
40
+ - lib/agnostic_presenters/mongomapper.rb
41
+ - lib/base.rb
42
+ - lib/proxen.rb
43
+ - lib/rails-init.rb
44
+ - spec/agnostic_presenters_spec.rb
45
+ - spec/proxen_spec.rb
46
+ has_rdoc: true
47
+ homepage: http://github.com/yeastymobs/agnostic_presenters
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options:
52
+ - --charset=UTF-8
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.7
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Agnostic Presenters
80
+ test_files:
81
+ - spec/agnostic_presenters_spec.rb
82
+ - spec/proxen_spec.rb