agnostic_presenters 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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