-
Notifications
You must be signed in to change notification settings - Fork 0
/
MODS
167 lines (114 loc) · 4.93 KB
/
MODS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
= Crabgrass Mods
This is documentation specific to Crabgras Mods.
* Controllers & Helpers: See Engines::RailsExtensions::Dependencies for more information.
* Views: now handled almost entirely by ActionView itself (see Engines::Plugin#add_plugin_view_paths for more information)
* Models: this is tricky, see below.
See http://api.rails-engines.org
== Overriding Views in a Mod
If this is in the init.rb of your plugin:
self.override_views = true
Then any view file in the plugin's app/views folder will override what
is in the main application. Otherwise, the views in the main
application take precedence. If <tt>load_once</tt> is set to false, then you
can make changes to your mod in development mod and see those changes
reflected in the application without needing to restart script/server.
== View Listeners
Suppose the main application had this in a view:
<%= call_hook :menu, :some_variable => true %>
You can create a listener for this hook in your plugin. When this hook
is called, all the listeners for that hook are called and their output
returned.
In your plugin's init.rb:
Dispatcher.to_prepare do
require 'my_listener'
end
In your plugin's lib/my_listener.rb
class MyListener < Crabgrass::Hook::ViewListener
include Singleton
def menu(context)
'hi' if context[:some_variable] == true
end
end
In this case, the 'call_hook' command will return 'hi'.
In your ViewListener, you can use any method that is available to a
view.
== Modifying Models in the Plugin
Some plugins have a problem: if a plugin applies a mixing directly to
a model in app/models, this mixin gets unloaded by rails after the
first request. This only happens in development mode. The symptom is
an application that works for the first request but fails on
subsiquent requests.
Normal plugins don't have this problem: they modify active record, and
then the core models call these extensions which triggers the
reloading of the plugin mixin.
To get around this problem, crabgrass has a method called
apply_mixin_to_model that is available in the plugin's init.rb.
(1) in your plugin's init.rb:
# in this example, there is a model app/models/language.rb, and module
# vendor/plugins/myplugin/app/models/language_extension.rb
apply_mixin_to_model(Language, LanguageExtension)
(2) define a module that will be used to extend the model:
module LanguageExtension
module InstanceMethods
def percent_complete()
count = Key.count_all
if count > 0
(Key.translated(self).count / count * 100.0).round.to_s + '%'
end
end
end
module ClassMethods
def hello
'i am a class method'
end
end
def self.add_to_class_definition
lambda do
has_many :translations, :dependent => :destroy
end
end
end
(3) Some alternate examples
Sometimes, the apply_mixin_to_model needs to be wrapped in Dispatcher.to_prepare.
This does two things: first, it applies the code block later in init process;
second, the code block gets re-applied on every request in development mode.
Sometimes, development mode will unload classes that you still want around, but
if you wrap in Dispatcher.to_prepare, then your code will still work.
Dispatcher.to_prepare do
apply_mixin_to_model(User, CracklibUserExtension)
end
You can also just extend a class directly:
Dispatcher.to_prepare do
User.class_eval do
validates_each(:password, :if => :password_required?) do |record, attr, value|
result = Cracklib.check(value)
unless result == "OK"
code = Cracklib.translation_key_from_error_message(result)
record.errors.add attr, result[code]
end
end
end
end
Some further reading....
Plugins loaded first problem:
* http://weblog.techno-weenie.net/2007/1/24/understanding-the-rails-initialization-process
Plugin classes unloaded after first refresh in development mode:
* http://weblog.techno-weenie.net/2007/1/26/understanding-the-rails-plugin-initialization-process
* http://rails-engines.org/development/common-issues-when-overloading-code-from-plugins/
* http://blog.nanorails.com/articles/2007/2/15/after_method
* http://www.ruby-forum.com/topic/111410
Reloading changed code problem:
* http://cameronyule.com/2008/07/make-rails-engines-2-reload-in-development-mode
* crabgrass has its own method. see lib/extends_to_engines.rb
== Customizing the stylesheets and javascript from a plugin
Simply define a listener for html_head. Whatever you return will get inserted there.
For example, <tt>mods/mymod/lib/my_view_listener.rb</tt>:
class MyViewListener < Crabgrass::Hook::ViewListener
include Singleton
def html_head(context)
return unless params[:controller] == 'whatever'
stylesheet_link_tag('mycss', :plugin => 'mymod')
end
end
Alternately, you could specify custom javascript or a favicon.
In this case, the stylesheet_link_tag will link to the file <tt>mods/mymod/assets/stylesheets/mycss.css</tt>