genki-merb_component 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +37 -9
- data/Rakefile +1 -1
- data/lib/merb_component/controller_ext.rb +132 -24
- data/lib/merb_component/resource_ext.rb +6 -2
- data/lib/merb_component/router_ext.rb +8 -0
- data/lib/merb_component.rb +1 -2
- data/spec/admin_spec.rb +96 -0
- data/spec/fixture/app/controllers/admin.rb +2 -3
- data/spec/fixture/app/controllers/comments.rb +6 -16
- data/spec/fixture/app/controllers/posts.rb +1 -0
- data/spec/fixture/app/models/comment.rb +2 -0
- data/spec/fixture/app/views/admin/show.html.erb +4 -0
- data/spec/fixture/app/views/comments/edit.html.erb +11 -0
- data/spec/fixture/app/views/comments/index.html.erb +3 -1
- data/spec/fixture/app/views/comments/new.html.erb +3 -0
- data/spec/fixture/app/views/layout/application.html.erb +11 -0
- data/spec/fixture/app/views/posts/show.html.erb +3 -5
- data/spec/fixture/config/router.rb +5 -8
- data/spec/merb_component_spec.rb +16 -2
- data/spec/posts_spec.rb +62 -2
- data/spec/spec_helper.rb +2 -0
- metadata +9 -5
- data/lib/merb_component/helper_ext.rb +0 -4
- data/lib/merb_component/model_ext.rb +0 -26
data/README
CHANGED
@@ -5,14 +5,42 @@ Merb plugin that provides composition of controllers.
|
|
5
5
|
|
6
6
|
Example of use:
|
7
7
|
|
8
|
-
|
9
|
-
<%= component User, :show, :id => 2 %>
|
8
|
+
In config/router.rb:
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
resources :posts do |post|
|
11
|
+
post.aggregates :comments
|
12
|
+
end
|
13
|
+
resource :admin, :controller => :admin do |admin|
|
14
|
+
admin.aggregates :comments
|
15
|
+
end
|
15
16
|
|
16
|
-
In
|
17
|
-
|
18
|
-
|
17
|
+
In controllers:
|
18
|
+
|
19
|
+
class Posts < Application
|
20
|
+
aggregates :comments
|
21
|
+
|
22
|
+
class Admin < Application
|
23
|
+
aggregates :show => :comments
|
24
|
+
|
25
|
+
In views:
|
26
|
+
|
27
|
+
Content of the user (id is 2) goes here
|
28
|
+
<%= component Users, :show, :id => 2 %>
|
29
|
+
|
30
|
+
Or, you can use symbol to specify subsidiary controller
|
31
|
+
<%= component :users, :show, :id => 2 %>
|
32
|
+
|
33
|
+
Index of posts related with the @user go here
|
34
|
+
<% Post.related_with @user do %>
|
35
|
+
<%= component Posts, :index %>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
In app/views/posts/index.html.erb,
|
39
|
+
You can access to the corresponding relation like this
|
40
|
+
<%= Post.relation #=> @user %>
|
41
|
+
|
42
|
+
For detail, you can see spec/fixture as an example.
|
43
|
+
|
44
|
+
Enjoy!
|
45
|
+
|
46
|
+
Genki Takiuchi
|
data/Rakefile
CHANGED
@@ -7,59 +7,167 @@ class Merb::Controller
|
|
7
7
|
|
8
8
|
class << self
|
9
9
|
private
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
def is_component(resource = nil)
|
11
|
+
resource = controller_name.singular if resource.nil?
|
12
|
+
r = resource.to_s
|
13
|
+
m = r.camel_case
|
14
|
+
iv = proc{|i| "(@#{r} = #{m}.#{i})"}
|
15
|
+
ivs = proc{|i| "(@#{r.pluralize} = #{m}.#{i})"}
|
16
|
+
class_eval <<-"RUBY"
|
17
|
+
def index; display #{ivs["all"]} end
|
18
|
+
def show(id) display #{iv["get(id)"]} end
|
19
|
+
def new; display #{iv["new"]} end
|
20
|
+
def edit(id) display #{iv["get(id)"]} end
|
21
|
+
def create(#{r}) #{iv["create(#{r})"]} end
|
22
|
+
def update(id,#{r}) #{iv["get(id)"]}.update_attributes(#{r}) end
|
23
|
+
def destroy(id) #{iv["get(id)"]}.destroy end
|
24
|
+
RUBY
|
25
|
+
end
|
26
|
+
|
27
|
+
def aggregates(aggregation, options = {})
|
28
|
+
if aggregation.is_a?(Symbol)
|
29
|
+
aggregation = {:show => aggregation}
|
30
|
+
end
|
31
|
+
aggregation.each do |agg_action, arg|
|
14
32
|
define_method(arg){} unless method_defined?(arg)
|
15
33
|
model = Object.full_const_get(arg.to_s.singular.camel_case)
|
16
34
|
key = "#{controller_name.singular}_id"
|
17
35
|
var = "@#{arg.to_s.singular}"
|
18
36
|
|
19
37
|
add_filter(_before_filters, proc{|c|
|
20
|
-
# setup request
|
21
38
|
id = params.delete(key)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
39
|
+
method = request.method
|
40
|
+
scope = Mash.new
|
41
|
+
scope[key] = id if id
|
42
|
+
object = nil
|
43
|
+
if action = METHOD_TO_ACTION[method]
|
44
|
+
# setup request
|
45
|
+
req = request.dup
|
46
|
+
req.reset_params!
|
47
|
+
req.instance_variable_set(:@params,
|
48
|
+
params.merge(:controller => arg, :action => action))
|
49
|
+
|
50
|
+
# call action of subsidiary controller with scope
|
51
|
+
cc = Object.full_const_get(params[:action].camel_case).new(req)
|
52
|
+
model.send :with_scope, scope do
|
53
|
+
begin
|
54
|
+
layout = cc.class.default_layout
|
55
|
+
cc.class.layout(options[:layout])
|
56
|
+
response = cc._abstract_dispatch(action)
|
57
|
+
ensure
|
58
|
+
cc.class.layout(layout)
|
59
|
+
end
|
60
|
+
object = cc.instance_variable_get(var)
|
61
|
+
c.instance_variable_set(var, object)
|
62
|
+
object = model.new if object.errors.empty?
|
63
|
+
end
|
64
|
+
elsif params[:id]
|
65
|
+
# GET with component id
|
66
|
+
object = model.get(params[:id])
|
67
|
+
c.instance_variable_set(var, object)
|
33
68
|
end
|
69
|
+
c.instance_variable_set("#{var}_component", object)
|
34
70
|
|
35
71
|
# prepare for performing actoin of principal controller
|
36
|
-
params[:id] = id
|
37
|
-
params[:action] = c.action_name =
|
72
|
+
c.params[:id] = id if id
|
73
|
+
c.params[:action] = c.action_name = agg_action.to_s
|
38
74
|
}, :only => arg)
|
39
75
|
end
|
40
76
|
end
|
41
77
|
end
|
42
78
|
|
79
|
+
class Aggregator
|
80
|
+
attr_reader :controller, :object, :result
|
81
|
+
|
82
|
+
def initialize(context, controller, &block)
|
83
|
+
@context = context
|
84
|
+
@controller = controller
|
85
|
+
@agg_name = @context.controller_name.singular.intern
|
86
|
+
model_class = Object.full_const_get(controller.name.singular)
|
87
|
+
@object = @context.instance_variable_get("@#{@agg_name}")
|
88
|
+
@scope = {}
|
89
|
+
|
90
|
+
if @object
|
91
|
+
relationship = model_class.relationships[@agg_name]
|
92
|
+
key_names = relationship.child_key.map{|i| i.name}
|
93
|
+
@scope = Hash[key_names.zip(@object.key)] if @object
|
94
|
+
end
|
95
|
+
|
96
|
+
@result = begin
|
97
|
+
Thread.critical = true
|
98
|
+
aggregators = Thread::current[:aggregators] ||= {}
|
99
|
+
(aggregators[controller] ||= []).push(self)
|
100
|
+
if model_class.respond_to?(:with_scope)
|
101
|
+
model_class.send(:with_scope, @scope, &block)
|
102
|
+
else
|
103
|
+
block.call
|
104
|
+
end
|
105
|
+
ensure
|
106
|
+
aggregators[controller].pop
|
107
|
+
Thread.critical = false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def key
|
112
|
+
@object || @agg_name
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
43
116
|
def _abstract_dispatch(*args)
|
44
117
|
_dispatch = Merb::AbstractController.instance_method(:_dispatch)
|
45
118
|
_dispatch.bind(self).call(*args)
|
46
119
|
end
|
47
120
|
|
121
|
+
def aggregator
|
122
|
+
aggregators = Thread::current[:aggregators] ||= {}
|
123
|
+
(aggregators[self.class] ||= []).last
|
124
|
+
end
|
125
|
+
|
48
126
|
private
|
49
127
|
def component(controller, action, params = {})
|
128
|
+
var = "@#{controller.to_s.singular}"
|
129
|
+
object = instance_variable_get("#{var}_component")
|
130
|
+
controller = Object.full_const_get(controller.to_s.camel_case)
|
50
131
|
req = request.dup
|
51
132
|
req.reset_params!
|
52
133
|
req.instance_variable_set :@params, params
|
53
|
-
|
134
|
+
|
135
|
+
Aggregator.new(self, controller) do
|
136
|
+
controller.new(req)._dispatch(action).instance_eval do
|
137
|
+
if object
|
138
|
+
original = instance_variable_get(var)
|
139
|
+
object.attributes = original.attributes if original
|
140
|
+
instance_variable_set(var, object)
|
141
|
+
end
|
142
|
+
render :layout => false
|
143
|
+
end
|
144
|
+
end.result
|
145
|
+
end
|
146
|
+
|
147
|
+
def form_for_component(controller, params = {}, &block)
|
148
|
+
var = "@#{controller.to_s.singular}"
|
149
|
+
object = instance_variable_get(var)
|
150
|
+
return nil if object.nil?
|
151
|
+
object = instance_variable_get("#{var}_component") || object
|
152
|
+
if object.new_record?
|
153
|
+
component(controller, :new, params)
|
154
|
+
else
|
155
|
+
component(controller, :edit, {:id => object.id}.merge(params))
|
156
|
+
end
|
54
157
|
end
|
55
158
|
|
56
159
|
def resource(first, *args)
|
57
|
-
|
160
|
+
return super unless aggregator
|
161
|
+
|
162
|
+
controller = case first
|
58
163
|
when Symbol, String
|
59
|
-
Object.full_const_get(first.to_s.
|
60
|
-
else
|
164
|
+
Object.full_const_get(first.to_s.camel_case)
|
165
|
+
else
|
166
|
+
Object.full_const_get(first.class.to_s.pluralize.camel_case)
|
61
167
|
end
|
62
|
-
|
63
|
-
super
|
168
|
+
|
169
|
+
return super unless controller <=> aggregator.controller
|
170
|
+
return super unless key = aggregator.key
|
171
|
+
super(key, first, *args)
|
64
172
|
end
|
65
173
|
end
|
@@ -1,7 +1,11 @@
|
|
1
1
|
module DataMapper::Resource
|
2
2
|
module ClassMethods
|
3
|
-
|
4
|
-
|
3
|
+
# set scope
|
4
|
+
def new(attrs = {})
|
5
|
+
collection = all
|
6
|
+
collection.repository.scope do
|
7
|
+
super(collection.default_attributes.merge(attrs))
|
8
|
+
end
|
5
9
|
end
|
6
10
|
end
|
7
11
|
end
|
data/lib/merb_component.rb
CHANGED
@@ -9,9 +9,8 @@ if defined?(Merb::Plugins)
|
|
9
9
|
Merb::BootLoader.before_app_loads do
|
10
10
|
# require code that must be loaded before the application
|
11
11
|
require 'merb_component/controller_ext'
|
12
|
-
require 'merb_component/model_ext'
|
13
12
|
require 'merb_component/resource_ext'
|
14
|
-
require 'merb_component/
|
13
|
+
require 'merb_component/router_ext'
|
15
14
|
end
|
16
15
|
|
17
16
|
Merb::BootLoader.after_app_loads do
|
data/spec/admin_spec.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Admin do
|
4
|
+
before do
|
5
|
+
@req = Merb::Request.new(
|
6
|
+
Merb::Const::REQUEST_PATH => "/admin",
|
7
|
+
Merb::Const::REQUEST_METHOD => "GET",
|
8
|
+
Merb::Const::QUERY_STRING => "")
|
9
|
+
@c = Admin.new(@req)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should be a controller" do
|
13
|
+
@c.should be_kind_of(Merb::Controller)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "Admin controller" do
|
18
|
+
before :all do
|
19
|
+
@post = Post.create
|
20
|
+
@comment = @post.comments.create(:body => "test")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should be tested on at least one post and comment" do
|
24
|
+
Post.count.should > 0
|
25
|
+
Comment.count.should > 0
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should index html" do
|
29
|
+
res = request(resource(:admin))
|
30
|
+
res.should be_successful
|
31
|
+
res.should have_xpath("//h1")
|
32
|
+
res.should have_xpath("//h2")
|
33
|
+
res.should have_xpath("//ul/li")
|
34
|
+
res.should_not have_xpath("//form")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should show html after update a comment" do
|
38
|
+
comment = @post.comments.last
|
39
|
+
comment.should be_kind_of(Comment)
|
40
|
+
res = request(resource(:admin, comment),
|
41
|
+
:method => 'PUT', :params => {:comment => {:body => "bar"}})
|
42
|
+
res.should be_successful
|
43
|
+
res.should have_xpath("//h1")
|
44
|
+
res.should have_xpath("//h2")
|
45
|
+
res.should have_xpath("//ul/li[1]")
|
46
|
+
res.should have_xpath("//form[@action='/admin/comments']")
|
47
|
+
res.should have_xpath("//form[@method='post']")
|
48
|
+
res.should_not have_xpath("//input[@value='put']")
|
49
|
+
res.should contain("bar")
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should show html after failed to update a comment" do
|
53
|
+
comment = @post.comments.last
|
54
|
+
comment.should be_kind_of(Comment)
|
55
|
+
res = request(resource(:admin, comment),
|
56
|
+
:method => 'PUT', :params => {:comment => {:body => ""}})
|
57
|
+
res.should be_successful
|
58
|
+
res.should have_xpath("//h1")
|
59
|
+
res.should have_xpath("//h2")
|
60
|
+
res.should have_xpath("//ul/li[1]")
|
61
|
+
res.should have_xpath("//form[@action='/admin/comments/#{comment.id}']")
|
62
|
+
res.should have_xpath("//form[@method='post']")
|
63
|
+
res.should have_xpath("//input[@value='put']")
|
64
|
+
res.should have_tag("div.error")
|
65
|
+
res.should have_tag("input.error[@name='comment[body]']")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should show html after delete a comment" do
|
69
|
+
count = @post.comments.count
|
70
|
+
comment = @post.comments.last
|
71
|
+
comment.should be_kind_of(Comment)
|
72
|
+
res = request(resource(:admin, comment), :method => 'DELETE')
|
73
|
+
res.should be_successful
|
74
|
+
res.should have_xpath("//h1")
|
75
|
+
res.should have_xpath("//h2")
|
76
|
+
res.should have_xpath("//form")
|
77
|
+
res.should have_xpath("//form[@action='/admin/comments']")
|
78
|
+
res.should have_xpath("//form[@method='post']")
|
79
|
+
res.should_not have_xpath("//input[@value='put']")
|
80
|
+
res.should_not have_xpath("//body/meta")
|
81
|
+
@post.comments.count.should == count - 1
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should show html after show a comment" do
|
85
|
+
comment = @post.comments.create(:body => "hello")
|
86
|
+
comment.should_not be_new_record
|
87
|
+
res = request(resource(:admin, comment), :method => 'GET')
|
88
|
+
res.should be_successful
|
89
|
+
res.should have_xpath("//h1")
|
90
|
+
res.should have_xpath("//h2")
|
91
|
+
res.should have_xpath("//form[@action='/admin/comments/#{comment.id}']")
|
92
|
+
res.should have_xpath("//form[@method='post']")
|
93
|
+
res.should have_xpath("//input[@value='put']")
|
94
|
+
res.should_not have_xpath("//body/meta")
|
95
|
+
end
|
96
|
+
end
|
@@ -1,25 +1,15 @@
|
|
1
1
|
class Comments < Application
|
2
|
-
|
3
|
-
@comments = Comment.all
|
4
|
-
display @comments
|
5
|
-
end
|
2
|
+
is_component :comment
|
6
3
|
|
7
4
|
def new
|
8
|
-
@comment = Comment.
|
5
|
+
@comment = Comment.new
|
6
|
+
@comment.body = "new"
|
9
7
|
display @comment
|
10
8
|
end
|
11
9
|
|
12
|
-
def
|
13
|
-
@comment = Comment.all.create(comment)
|
14
|
-
end
|
15
|
-
|
16
|
-
def update(id, comment)
|
10
|
+
def edit(id)
|
17
11
|
@comment = Comment.get(id)
|
18
|
-
@comment.
|
19
|
-
|
20
|
-
|
21
|
-
def destroy(id)
|
22
|
-
@comment = Comment.get(id)
|
23
|
-
@comment.destroy
|
12
|
+
@comment.body = "edit"
|
13
|
+
display @comment
|
24
14
|
end
|
25
15
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<h3>
|
2
|
+
Edit Comment of
|
3
|
+
<%= link_to h(@comment.post.id), resource(@comment.post) %>
|
4
|
+
</h3>
|
5
|
+
|
6
|
+
<%= error_messages_for @comment %>
|
7
|
+
|
8
|
+
<%= form_for @comment, :action => resource(@comment), :method => :put do %>
|
9
|
+
<%= text_field :body %>
|
10
|
+
<%= submit "Submit" %>
|
11
|
+
<% end =%>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml"
|
3
|
+
xmlns:v="urn:schemas-microsoft-com:vml" xml:lang="en-us" lang="en-us">
|
4
|
+
<head>
|
5
|
+
<title>merb_component</title>
|
6
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<%= catch_content :for_layout %>
|
10
|
+
</body>
|
11
|
+
</html>
|
@@ -1,6 +1,4 @@
|
|
1
|
-
<h1><%= @post.id %></h1>
|
1
|
+
<h1><%= link_to @post.id.to_s, resource(@post) %></h1>
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
<%= component Comments, :new %>
|
6
|
-
<% end %>
|
3
|
+
<%= component Comments, :index %>
|
4
|
+
<%= form_for_component :comments %>
|
@@ -1,13 +1,10 @@
|
|
1
1
|
Merb::Router.prepare do
|
2
|
-
resources :
|
3
|
-
|
4
|
-
|
5
|
-
posts.resources :comments#, :controller => :posts
|
2
|
+
resources :comments
|
3
|
+
resources :posts do |post|
|
4
|
+
post.aggregates :comments
|
6
5
|
end
|
7
|
-
resource :admin do |admin|
|
8
|
-
admin.
|
9
|
-
admin.match("/:action/:id").to(:controller => :admin)
|
10
|
-
admin.resources :comments#, :controller => :admin
|
6
|
+
resource :admin, :controller => :admin do |admin|
|
7
|
+
admin.aggregates :comments
|
11
8
|
end
|
12
9
|
|
13
10
|
default_routes
|
data/spec/merb_component_spec.rb
CHANGED
@@ -3,9 +3,23 @@ require File.dirname(__FILE__) + '/spec_helper'
|
|
3
3
|
describe "merb_component" do
|
4
4
|
it "should extend controller" do
|
5
5
|
Posts.private_instance_methods.should be_include("component")
|
6
|
+
Posts.private_instance_methods.should be_include("form_for_component")
|
7
|
+
Posts.instance_methods.should be_include("aggregator")
|
6
8
|
end
|
7
9
|
|
8
|
-
it "should extend model" do
|
9
|
-
Post.public_methods.
|
10
|
+
it "should not extend model" do
|
11
|
+
Post.public_methods.should_not be_include("related_with")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should accept controller class as the first param of compoent" do
|
15
|
+
req = Merb::Request.new({})
|
16
|
+
result = Posts.new(req).send(:component, Comments, :index)
|
17
|
+
result.should be_kind_of(String)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should accept symbol as the first param of compoent" do
|
21
|
+
req = Merb::Request.new({})
|
22
|
+
result = Posts.new(req).send(:component, :comments, :index)
|
23
|
+
result.should be_kind_of(String)
|
10
24
|
end
|
11
25
|
end
|
data/spec/posts_spec.rb
CHANGED
@@ -17,7 +17,7 @@ end
|
|
17
17
|
describe "Posts controller" do
|
18
18
|
before :all do
|
19
19
|
@post = Post.create
|
20
|
-
@comment = @post.comments.create
|
20
|
+
@comment = @post.comments.create(:body => "test")
|
21
21
|
end
|
22
22
|
|
23
23
|
it "should be tested on at least one post" do
|
@@ -33,6 +33,8 @@ describe "Posts controller" do
|
|
33
33
|
res.should have_xpath("//ul/li")
|
34
34
|
res.should have_xpath("//form[@method='post']")
|
35
35
|
res.should have_xpath("//form[@action='/posts/#{@post.id}/comments']")
|
36
|
+
res.should_not have_xpath("//input[@value='put']")
|
37
|
+
res.should have_xpath("//input[@value='new']")
|
36
38
|
end
|
37
39
|
|
38
40
|
it "should show html after post a comment" do
|
@@ -45,10 +47,29 @@ describe "Posts controller" do
|
|
45
47
|
res.should have_xpath("//ul/li[1]")
|
46
48
|
res.should have_xpath("//ul/li[2]")
|
47
49
|
res.should have_xpath("//form[@method='post']")
|
50
|
+
res.should have_xpath("//form[@action='/posts/#{@post.id}/comments']")
|
51
|
+
res.should_not have_xpath("//input[@value='put']")
|
48
52
|
res.should contain("foo")
|
49
53
|
Comment.all(:post_id => @post.id).count.should == count + 1
|
50
54
|
end
|
51
55
|
|
56
|
+
it "should show html after failed to post a comment" do
|
57
|
+
count = Comment.all(:post_id => @post.id).count
|
58
|
+
res = request(resource(@post, :comments),
|
59
|
+
:method => 'POST', :params => {:comment => {:body => ""}})
|
60
|
+
res.should be_successful
|
61
|
+
res.should have_xpath("//h1")
|
62
|
+
res.should have_xpath("//h2")
|
63
|
+
res.should have_xpath("//ul/li[1]")
|
64
|
+
res.should have_xpath("//ul/li[2]")
|
65
|
+
res.should have_xpath("//form[@method='post']")
|
66
|
+
res.should have_xpath("//form[@action='/posts/#{@post.id}/comments']")
|
67
|
+
res.should_not have_xpath("//input[@value='put']")
|
68
|
+
res.should have_tag("div.error")
|
69
|
+
res.should have_tag("input.error[@name='comment[body]']")
|
70
|
+
Comment.all(:post_id => @post.id).count.should == count
|
71
|
+
end
|
72
|
+
|
52
73
|
it "should show html after update a comment" do
|
53
74
|
comment = @post.comments.last
|
54
75
|
comment.should be_kind_of(Comment)
|
@@ -60,10 +81,30 @@ describe "Posts controller" do
|
|
60
81
|
res.should have_xpath("//ul/li[1]")
|
61
82
|
res.should have_xpath("//ul/li[2]")
|
62
83
|
res.should have_xpath("//form[@method='post']")
|
84
|
+
res.should have_xpath("//form[@action='/posts/#{@post.id}/comments']")
|
85
|
+
res.should_not have_xpath("//input[@value='put']")
|
63
86
|
res.should contain("bar")
|
64
87
|
end
|
65
88
|
|
66
|
-
it "should show html after update a comment" do
|
89
|
+
it "should show html after failed to update a comment" do
|
90
|
+
comment = @post.comments.last
|
91
|
+
comment.should be_kind_of(Comment)
|
92
|
+
res = request(resource(@post, comment),
|
93
|
+
:method => 'PUT', :params => {:comment => {:body => ""}})
|
94
|
+
res.should be_successful
|
95
|
+
res.should have_xpath("//h1")
|
96
|
+
res.should have_xpath("//h2")
|
97
|
+
res.should have_xpath("//ul/li[1]")
|
98
|
+
res.should have_xpath("//ul/li[2]")
|
99
|
+
res.should have_xpath("//form[@method='post']")
|
100
|
+
url = "/posts/#{@post.id}/comments/#{comment.id}"
|
101
|
+
res.should have_xpath("//form[@action='#{url}']")
|
102
|
+
res.should have_xpath("//input[@value='put']")
|
103
|
+
res.should have_tag("div.error")
|
104
|
+
res.should have_tag("input.error[@name='comment[body]']")
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should show html after delete a comment" do
|
67
108
|
count = @post.comments.count
|
68
109
|
comment = @post.comments.last
|
69
110
|
comment.should be_kind_of(Comment)
|
@@ -71,6 +112,25 @@ describe "Posts controller" do
|
|
71
112
|
res.should be_successful
|
72
113
|
res.should have_xpath("//h1")
|
73
114
|
res.should have_xpath("//h2")
|
115
|
+
res.should have_xpath("//form[@action='/posts/#{@post.id}/comments']")
|
116
|
+
res.should have_xpath("//form[@method='post']")
|
117
|
+
res.should_not have_xpath("//input[@value='put']")
|
74
118
|
@post.comments.count.should == count - 1
|
75
119
|
end
|
120
|
+
|
121
|
+
it "should show html after show a comment" do
|
122
|
+
comment = @post.comments.create(:body => "test")
|
123
|
+
comment.should_not be_new_record
|
124
|
+
res = request(resource(@post, comment), :method => 'GET')
|
125
|
+
res.should be_successful
|
126
|
+
res.should have_xpath("//h1")
|
127
|
+
res.should have_xpath("//h2")
|
128
|
+
url = "/posts/#{@post.id}/comments/#{comment.id}"
|
129
|
+
res.should have_xpath("//form[@action='#{url}']")
|
130
|
+
res.should have_xpath("//input[@value='put']")
|
131
|
+
res.should_not have_xpath("//body/meta")
|
132
|
+
res.should have_xpath("//input[@value='edit']")
|
133
|
+
|
134
|
+
pending "should check pagination"
|
135
|
+
end
|
76
136
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: genki-merb_component
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Genki Takiuchi
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
12
|
+
date: 2009-01-26 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -38,11 +38,11 @@ files:
|
|
38
38
|
- TODO
|
39
39
|
- lib/merb_component
|
40
40
|
- lib/merb_component/controller_ext.rb
|
41
|
-
- lib/merb_component/helper_ext.rb
|
42
41
|
- lib/merb_component/merbtasks.rb
|
43
|
-
- lib/merb_component/model_ext.rb
|
44
42
|
- lib/merb_component/resource_ext.rb
|
43
|
+
- lib/merb_component/router_ext.rb
|
45
44
|
- lib/merb_component.rb
|
45
|
+
- spec/admin_spec.rb
|
46
46
|
- spec/fixture
|
47
47
|
- spec/fixture/app
|
48
48
|
- spec/fixture/app/controllers
|
@@ -54,15 +54,19 @@ files:
|
|
54
54
|
- spec/fixture/app/models/comment.rb
|
55
55
|
- spec/fixture/app/models/post.rb
|
56
56
|
- spec/fixture/app/views
|
57
|
+
- spec/fixture/app/views/admin
|
58
|
+
- spec/fixture/app/views/admin/show.html.erb
|
57
59
|
- spec/fixture/app/views/comments
|
60
|
+
- spec/fixture/app/views/comments/edit.html.erb
|
58
61
|
- spec/fixture/app/views/comments/index.html.erb
|
59
62
|
- spec/fixture/app/views/comments/new.html.erb
|
63
|
+
- spec/fixture/app/views/layout
|
64
|
+
- spec/fixture/app/views/layout/application.html.erb
|
60
65
|
- spec/fixture/app/views/posts
|
61
66
|
- spec/fixture/app/views/posts/index.html.erb
|
62
67
|
- spec/fixture/app/views/posts/show.html.erb
|
63
68
|
- spec/fixture/config
|
64
69
|
- spec/fixture/config/router.rb
|
65
|
-
- spec/fixture/tmp
|
66
70
|
- spec/merb_component_spec.rb
|
67
71
|
- spec/posts_spec.rb
|
68
72
|
- spec/spec_helper.rb
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module DataMapper::Model
|
2
|
-
def related_with(model, &block)
|
3
|
-
model_class = model.class
|
4
|
-
storage_name = model_class.storage_name
|
5
|
-
assoc_name = storage_name.singular.intern
|
6
|
-
key_names = relationships[assoc_name].child_key.map{|i| i.name}
|
7
|
-
push_relation(model)
|
8
|
-
with_scope(Hash[key_names.zip(model.key)], &block)
|
9
|
-
ensure
|
10
|
-
pop_relation
|
11
|
-
end
|
12
|
-
|
13
|
-
def relation
|
14
|
-
(Thread::current[:relation] || []).last
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
def push_relation(relation)
|
19
|
-
Thread::current[:relation] ||= []
|
20
|
-
Thread::current[:relation].push relation
|
21
|
-
end
|
22
|
-
|
23
|
-
def pop_relation
|
24
|
-
Thread::current[:relation].pop
|
25
|
-
end
|
26
|
-
end
|