rails-and-solid 0.9.0 → 0.9.1
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/README +185 -8
- data/lib/rails-and-solid.rb +11 -0
- data/lib/rails-and-solid/handler/instantiate.rb +3 -3
- data/lib/rails-and-solid/version.rb +1 -1
- metadata +3 -3
data/README
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
# What is this?
|
2
|
+
|
3
|
+
This is a gem that changes Rails behavior to allow you to code more freely, without coupling
|
4
|
+
yourself to inheritance issues. Take a look at the examples to see what it changes.
|
5
|
+
|
1
6
|
# Installing
|
2
7
|
|
3
8
|
Configure the gem in your Gemfile:
|
@@ -11,22 +16,194 @@ and make it believe you have the controller he wants, just trick him:
|
|
11
16
|
|
12
17
|
class ApplicationController < ActionController::Base
|
13
18
|
|
14
|
-
|
15
|
-
|
19
|
+
solidify
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
# Inheritance coupling in controllers
|
24
|
+
|
25
|
+
Ok. Time for some goodies. First example, let's unpimp :
|
26
|
+
|
27
|
+
[code]
|
28
|
+
RailsAndSolid.pimp :clients
|
29
|
+
|
30
|
+
class ClientsControl
|
31
|
+
|
32
|
+
def show(id)
|
33
|
+
@client = Client.find(id)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
[/code]
|
38
|
+
|
39
|
+
Notice that *id* is the parameter *id* from your http request. No inheritance required.
|
40
|
+
Same old behavior. No http params appeared. You can unit test for real, without all
|
41
|
+
its inherited behavior.
|
42
|
+
|
43
|
+
Why? Am I really coupled to something when inheriting?
|
44
|
+
|
45
|
+
[code]
|
46
|
+
rails console
|
47
|
+
> ActionController::Base.instance_methods.size
|
48
|
+
374
|
49
|
+
> (ActionController::Base.instance_methods + ActionController::Base.instance_methods - Object.instance_methods - Object.private_instance_methods).size
|
50
|
+
560
|
51
|
+
> ActionController::Base.methods.size
|
52
|
+
353
|
53
|
+
> ActionController::Base.ancestors.size
|
54
|
+
36
|
55
|
+
[/code]
|
56
|
+
|
57
|
+
By default, when creating a new controller in Rails, you are coupled
|
58
|
+
with 374 public methods (*542 in total*), inheriting behavior from *36 different* sources.
|
59
|
+
|
60
|
+
# Session parameters
|
61
|
+
|
62
|
+
[code]
|
63
|
+
class ClientsControl
|
64
|
+
|
65
|
+
def show(current_user, id)
|
66
|
+
# if session[:current_user] exists, guess who is here?
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
[/code]
|
71
|
+
|
72
|
+
Again, unit testable. Non-http intrusive.
|
73
|
+
|
74
|
+
# Session parameters
|
75
|
+
|
76
|
+
[code]
|
77
|
+
class ClientsControl
|
78
|
+
|
79
|
+
def show(current_user, id)
|
80
|
+
# if session[:current_user] exists, guess who is here?
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
[/code]
|
85
|
+
|
86
|
+
# What about creating a client?
|
87
|
+
|
88
|
+
Well, just create it:
|
89
|
+
|
90
|
+
[code]
|
91
|
+
class ClientsControl
|
16
92
|
|
93
|
+
def create(client)
|
94
|
+
client.save
|
17
95
|
end
|
96
|
+
|
97
|
+
end
|
98
|
+
[/code]
|
99
|
+
|
100
|
+
# But you can't load, right?
|
101
|
+
|
102
|
+
I don't think so:
|
103
|
+
|
104
|
+
[code]
|
105
|
+
class ClientsControl
|
106
|
+
|
107
|
+
def show(loaded_client)
|
108
|
+
@client = loaded_client
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
[/code]
|
18
113
|
|
19
|
-
#
|
114
|
+
# But what about responses? Sessions? Http stuff?
|
20
115
|
|
21
|
-
|
116
|
+
Just bind to what you need, nothing more than that:
|
117
|
+
|
118
|
+
[code]
|
119
|
+
def logout(session, redirect_to, flash)
|
120
|
+
session[:current_user] = nil
|
121
|
+
flash[:message] = "Logged out..."
|
122
|
+
redirect_to.root_path
|
123
|
+
end
|
124
|
+
[/code]
|
125
|
+
|
126
|
+
# Internally: Helpers
|
127
|
+
|
128
|
+
Helpers are the classes that help passing parameters to those
|
129
|
+
methods by using a key based lookup algorithm. For example
|
130
|
+
if a 'json' parameter is received, one will use the Json helper
|
131
|
+
to return it:
|
132
|
+
|
133
|
+
[code]
|
134
|
+
class ApplicationController < ActionController::Base
|
135
|
+
|
136
|
+
protect_from_forgery
|
137
|
+
solidify
|
138
|
+
|
139
|
+
class JsonHelper
|
140
|
+
def initialize(controller)
|
141
|
+
@controller = controller
|
142
|
+
end
|
143
|
+
def message(x)
|
144
|
+
@controller.render :json => {'message' => x}
|
145
|
+
end
|
146
|
+
end
|
22
147
|
|
23
|
-
|
148
|
+
KEYS["json"] = JsonHelper
|
149
|
+
|
150
|
+
end
|
151
|
+
[/code]
|
24
152
|
|
25
|
-
|
153
|
+
Now you can receive json and use the message:
|
26
154
|
|
27
|
-
|
155
|
+
[code]
|
156
|
+
def some_action(json)
|
157
|
+
json.message("Well done!")
|
158
|
+
end
|
159
|
+
[/code]
|
28
160
|
|
29
|
-
|
161
|
+
You can use those auxiliary objects to compose any kind of behavior (not just rendering)
|
162
|
+
by just receiving the expected coupled semantic value instead of inheriting everything.
|
30
163
|
|
31
164
|
# Handlers
|
32
165
|
|
166
|
+
Handlers are not key-fixed parameter providers. For instance, the param lookup:
|
167
|
+
|
168
|
+
[code]
|
169
|
+
module RailsAndSolid
|
170
|
+
module Handler
|
171
|
+
class Param
|
172
|
+
def extract(controller, name)
|
173
|
+
controller.params[name]
|
174
|
+
end
|
175
|
+
def handles?(controller, name)
|
176
|
+
controller.params[name]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end[/code]
|
181
|
+
|
182
|
+
It will be asked whether it can handle this parameter and the provide it.
|
183
|
+
|
184
|
+
# TODO List
|
185
|
+
|
186
|
+
Help?
|
187
|
+
|
188
|
+
First, simple unit tests.
|
189
|
+
|
190
|
+
Units are split and behavior is composed. This was only to
|
191
|
+
show that its possible to achieve the same (and better results) with less coupling and a different
|
192
|
+
OO design approach. So what about start unit testing, providing new handlers, new helpers or
|
193
|
+
just doing a blog post on it? Refactoring trick_him would also be nice. Removing the
|
194
|
+
pimp method would be even better but that requires more refactoring on Rails internals,
|
195
|
+
not just tricking it.
|
196
|
+
|
197
|
+
Results from handlers "handles?" could be cached.
|
198
|
+
|
199
|
+
You can also implement DI support by correctly implementing di_instantiate(type) at TrickHim.
|
200
|
+
|
201
|
+
Finally, feel you can support named parameters in 1.8.7+ if not yet supported.
|
202
|
+
|
203
|
+
Everything is quite simple so far as this is just a concept
|
204
|
+
|
205
|
+
# Why not changing Rails internally?
|
206
|
+
|
207
|
+
One needs to know how all the mixin of Rails interact with each other (i.e. the 36 ancestors
|
208
|
+
of ActionController) in order to make safe changes. That is the coupling this proof of concept
|
209
|
+
is trying show how to avoid.
|
data/lib/rails-and-solid.rb
CHANGED
@@ -4,4 +4,15 @@ module RailsAndSolid
|
|
4
4
|
autoload :TrickHim, 'rails-and-solid/trick_him'
|
5
5
|
autoload :Handler, 'rails-and-solid/handler'
|
6
6
|
|
7
|
+
# Shortcut to hack rails.
|
8
|
+
def self.pimp(what)
|
9
|
+
Object.module_eval "class #{what.to_s.camelize}Controller < ApplicationController; end"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class ActionController::Base
|
15
|
+
def self.solidify
|
16
|
+
include RailsAndSolid::TrickHim
|
17
|
+
end
|
7
18
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module RailsAndSolid
|
2
2
|
module Handler
|
3
3
|
class Instantiate
|
4
|
-
def extract(controller,
|
5
|
-
val =
|
6
|
-
controller.send :instance_variable_set ,"@#{
|
4
|
+
def extract(controller, name)
|
5
|
+
val = name.camelize.constantize.new(controller.params[p])
|
6
|
+
controller.send :instance_variable_set ,"@#{name}", val
|
7
7
|
return val
|
8
8
|
end
|
9
9
|
def handles?(controller, name)
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 9
|
8
|
-
-
|
9
|
-
version: 0.9.
|
8
|
+
- 1
|
9
|
+
version: 0.9.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Guilherme Silveira
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-03-
|
17
|
+
date: 2011-03-18 00:00:00 -03:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|