winton-rails_widget 1.0.2 → 1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +20 -5
- data/lib/rails_widget.rb +99 -3
- data/lib/rails_widget/assets.rb +196 -86
- data/lib/rails_widget/widget.rb +148 -227
- data/lib/rails_widget/widgets.rb +188 -0
- metadata +6 -6
- data/tasks/rails_widget.rake +0 -31
data/README.markdown
CHANGED
@@ -1,8 +1,23 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
RailsWidget
|
2
|
+
===========
|
3
3
|
|
4
|
-
|
4
|
+
Allows you to group your client-side assets into distributable "widgets"
|
5
5
|
|
6
|
-
|
6
|
+
* Attach assets and render partials with a single <tt>widget</tt> call
|
7
|
+
* Configure widgets via an <tt>options.rb</tt> file
|
8
|
+
* Supported assets: flash, images, partials, javascripts, stylesheets, and textarea templates
|
7
9
|
|
8
|
-
|
10
|
+
|
11
|
+
Install
|
12
|
+
-------
|
13
|
+
|
14
|
+
script/plugin install git://github.com/winton/rails_widget.git
|
15
|
+
|
16
|
+
|
17
|
+
Documentation
|
18
|
+
--------------
|
19
|
+
|
20
|
+
[RDoc](http://wintoni.us/rails_widget) for more details.
|
21
|
+
|
22
|
+
|
23
|
+
##### Copyright © 2008 [Winton Welsh](mailto:mail@wintoni.us), released under the MIT license
|
data/lib/rails_widget.rb
CHANGED
@@ -1,7 +1,103 @@
|
|
1
|
+
# RailsWidget allows you to group your client-side assets into distributable "widgets"
|
2
|
+
#
|
3
|
+
# * Attach assets and render partials with a single <tt>widget</tt> call
|
4
|
+
# * Configure widgets via an <tt>options.rb</tt> file
|
5
|
+
# * Supports flash, images, javascripts, partials, stylesheets, and textarea templates
|
6
|
+
#
|
7
|
+
# == Example
|
8
|
+
# === What is a widget?
|
9
|
+
# Each directory in <tt>app/widgets</tt> is considered a widget. Let's make a widget called <tt>alert</tt>.
|
10
|
+
#
|
11
|
+
# app/
|
12
|
+
# widgets/
|
13
|
+
# alert/
|
14
|
+
# options.rb
|
15
|
+
# flash/
|
16
|
+
# images/
|
17
|
+
# javascripts/
|
18
|
+
# alert.js
|
19
|
+
# init.js
|
20
|
+
# partials/
|
21
|
+
# _init.html.erb
|
22
|
+
# stylesheets/
|
23
|
+
# init.css
|
24
|
+
# style.css
|
25
|
+
# templates/
|
26
|
+
#
|
27
|
+
# <b>Init</b> files are rendered directly into the layout (inline, dynamically generated).
|
28
|
+
#
|
29
|
+
# ==== options.rb
|
30
|
+
# { :id => 'alert', :message => 'Hello world!', :color => 'red' }
|
31
|
+
#
|
32
|
+
# ==== javascripts/alert.js
|
33
|
+
# function alertWidget(options) {
|
34
|
+
# alert(options.message);
|
35
|
+
# }
|
36
|
+
#
|
37
|
+
# ==== javascripts/init.js
|
38
|
+
# alertWidget(<%= options.to_json %>);
|
39
|
+
#
|
40
|
+
#
|
41
|
+
# ==== partials/_init.html.erb
|
42
|
+
# <div id="<%= id %>" class="alert">
|
43
|
+
# You just got alerted.
|
44
|
+
# </div>
|
45
|
+
#
|
46
|
+
# ==== stylesheets/init.css
|
47
|
+
# #<%= id %> { color:<%= color %>; }
|
48
|
+
#
|
49
|
+
# ==== stylesheets/style.css
|
50
|
+
# .alert { font-size:18px; }
|
51
|
+
#
|
52
|
+
# === Layout view
|
53
|
+
# <html>
|
54
|
+
# <head>
|
55
|
+
# <%= javascripts %>
|
56
|
+
# <%= stylesheets %>
|
57
|
+
# </head>
|
58
|
+
# <body>
|
59
|
+
# <%= yield %>
|
60
|
+
# </body>
|
61
|
+
# </html>
|
62
|
+
#
|
63
|
+
# === Action view
|
64
|
+
# <%= widget :alert, :id => 'alert1', :color => 'blue' %>
|
65
|
+
# <%= widget :alert, :id => 'alert2' %>
|
66
|
+
#
|
67
|
+
# === Resulting HTML
|
68
|
+
# <html>
|
69
|
+
# <head>
|
70
|
+
# <script src="/javascripts/widgets/alert/alert.js?1220593492" type="text/javascript"></script>
|
71
|
+
# <script type='text/javascript'>
|
72
|
+
# alertWidget({ id: 'alert', message: 'Hello world!', color: 'red' });
|
73
|
+
# </script>
|
74
|
+
# <link href="/stylesheets/widgets/alert/style.css?1220593492" media="screen" rel="stylesheet" type="text/css" />
|
75
|
+
# <style type="text/css">
|
76
|
+
# #alert1 { color:blue; }
|
77
|
+
# #alert2 { color:red; }
|
78
|
+
# </style>
|
79
|
+
# </head>
|
80
|
+
# <body>
|
81
|
+
# <div id="alert1" class="alert">
|
82
|
+
# You just got alerted.
|
83
|
+
# </div>
|
84
|
+
# <div id="alert2" class="alert">
|
85
|
+
# You just got alerted.
|
86
|
+
# </div>
|
87
|
+
# </body>
|
88
|
+
# </html>
|
89
|
+
#
|
90
|
+
# == Inheritance
|
91
|
+
#
|
92
|
+
module RailsWidget
|
93
|
+
end
|
94
|
+
|
1
95
|
Dir[File.expand_path('*/*.rb', File.dirname(__FILE__))].each do |f|
|
2
96
|
require [ File.dirname(f), File.basename(f, '.rb') ].join('/')
|
3
97
|
end
|
4
98
|
|
5
|
-
ActionView::Base.send :include,
|
6
|
-
ActionController::Base.send :include,
|
7
|
-
ActionController::Base.view_paths += [ RAILS_ROOT + '/app/widgets' ]
|
99
|
+
ActionView::Base.send :include, RailsWidget
|
100
|
+
ActionController::Base.send :include, RailsWidget
|
101
|
+
ActionController::Base.view_paths += [ RAILS_ROOT + '/app/widgets' ]
|
102
|
+
|
103
|
+
# :main:RailsWidget
|
data/lib/rails_widget/assets.rb
CHANGED
@@ -1,114 +1,224 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
def default_javascript
|
4
|
-
"#{params[:controller]}/#{params[:action]}"
|
5
|
-
end
|
6
|
-
|
7
|
-
def default_stylesheet
|
8
|
-
"#{params[:controller]}/#{params[:action]}"
|
9
|
-
end
|
1
|
+
module RailsWidget
|
10
2
|
|
3
|
+
# Adds or renders javascript assets based on whether parameters are given or not.
|
4
|
+
#
|
5
|
+
# ==== Layout view
|
6
|
+
# <html>
|
7
|
+
# <head>
|
8
|
+
# <%= javascripts %>
|
9
|
+
# </head>
|
10
|
+
# <%= yield %>
|
11
|
+
# </html>
|
12
|
+
#
|
13
|
+
# ==== Action view
|
14
|
+
# <% javascripts 'script1', 'script2' do -%>
|
15
|
+
# alert('Hello world!');
|
16
|
+
# <% end -%>
|
17
|
+
# Content goes here.
|
18
|
+
#
|
19
|
+
# ==== Resulting HTML
|
20
|
+
# <html>
|
21
|
+
# <head>
|
22
|
+
# <script src="/javascripts/script1.js?1220593492" type="text/javascript"></script>
|
23
|
+
# <script src="/javascripts/script2.js?1220593492" type="text/javascript"></script>
|
24
|
+
# <script type='text/javascript'>
|
25
|
+
# alert('Hello world!');
|
26
|
+
# </script>
|
27
|
+
# </head>
|
28
|
+
# Content goes here.
|
29
|
+
# </html>
|
30
|
+
#
|
31
|
+
# Calling <tt>javascripts</tt> with path parameters or a script block will store the asset for later rendering.
|
32
|
+
#
|
33
|
+
# Calling <tt>javascripts</tt> without parameters renders the assets in the order they were added.
|
34
|
+
#
|
35
|
+
# The method accepts all options supported by <tt>javascript_include_tag</tt>.
|
36
|
+
#
|
11
37
|
def javascripts(*paths, &block)
|
12
|
-
|
38
|
+
@assets ||= Assets.new binding, controller, logger
|
39
|
+
@assets.javascripts *paths, &block
|
13
40
|
end
|
14
41
|
|
42
|
+
# Adds or renders stylesheet assets based on whether parameters are given or not.
|
43
|
+
#
|
44
|
+
# ==== Layout example
|
45
|
+
# <html>
|
46
|
+
# <head>
|
47
|
+
# <%= stylesheets %>
|
48
|
+
# </head>
|
49
|
+
# <%= yield %>
|
50
|
+
# </html>
|
51
|
+
#
|
52
|
+
# ==== Action example
|
53
|
+
# <% stylesheets 'style1', 'style2' -%>
|
54
|
+
# Content goes here.
|
55
|
+
#
|
56
|
+
# ==== Result
|
57
|
+
# <html>
|
58
|
+
# <head>
|
59
|
+
# <link href="/stylesheets/style1.css?1224923418" media="screen" rel="stylesheet" type="text/css" />
|
60
|
+
# <link href="/stylesheets/style2.css?1224923418" media="screen" rel="stylesheet" type="text/css" />
|
61
|
+
# </head>
|
62
|
+
# Content goes here.
|
63
|
+
# </html>
|
64
|
+
#
|
65
|
+
# Calling <tt>stylesheets</tt> with path parameters will store the asset for later rendering.
|
66
|
+
#
|
67
|
+
# Calling <tt>stylesheets</tt> without parameters renders the assets in the order they were added.
|
68
|
+
#
|
69
|
+
# The method accepts all options supported by <tt>stylesheet_link_tag</tt>.
|
70
|
+
#
|
15
71
|
def stylesheets(*paths, &block)
|
16
|
-
|
72
|
+
@assets ||= Assets.new binding, controller, logger
|
73
|
+
@assets.stylesheets *paths, &block
|
17
74
|
end
|
18
75
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
76
|
+
# Adds or renders textarea-based templates based on whether parameters are given or not.
|
77
|
+
#
|
78
|
+
# Use this with something like PURE <http://beebole.com/pure> or TrimPath's JST <http://trimpath.com>.
|
79
|
+
#
|
80
|
+
# ==== Layout example
|
81
|
+
# <html>
|
82
|
+
# <%= yield %>
|
83
|
+
# <%= templates %>
|
84
|
+
# </html>
|
85
|
+
#
|
86
|
+
# ==== Action example
|
87
|
+
# <% templates :id => 'myid', :partial => 'some_action/partial', :locals => { :x => 'Hello world' } -%>
|
88
|
+
# <% templates do -%>
|
89
|
+
# Template goes here.
|
90
|
+
# <% end -%>
|
91
|
+
# Content goes here.
|
92
|
+
#
|
93
|
+
# ==== Partial example (<tt>some_action/partial</tt>)
|
94
|
+
# <%= x %>!
|
95
|
+
#
|
96
|
+
# ==== Result
|
97
|
+
# <html>
|
98
|
+
# Content goes here.
|
99
|
+
# <textarea id='template_myid' style='display:none'>
|
100
|
+
# Hello world!
|
101
|
+
# </textarea>
|
102
|
+
# <textarea id='template' style='display:none'>
|
103
|
+
# Template goes here.
|
104
|
+
# </textarea>
|
105
|
+
# </html>
|
106
|
+
#
|
107
|
+
# Calling <tt>templates</tt> with path parameters or a block will store the asset for later rendering.
|
108
|
+
#
|
109
|
+
# Calling <tt>templates</tt> without parameters renders the assets in the order they were added.
|
110
|
+
#
|
111
|
+
def templates(*options, &block)
|
112
|
+
@assets ||= Assets.new binding, controller, logger
|
113
|
+
@assets.templates *options, &block
|
31
114
|
end
|
32
115
|
|
33
|
-
|
34
|
-
|
35
|
-
def add_assets(type, paths, &block)
|
36
|
-
options = paths.extract_options!
|
37
|
-
paths.flatten! unless type == :templates
|
116
|
+
class Assets
|
117
|
+
attr :assets, true
|
38
118
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
119
|
+
# Used for eval access
|
120
|
+
attr :block, true
|
121
|
+
attr :params, true
|
122
|
+
attr :options, true
|
43
123
|
|
44
|
-
|
124
|
+
def initialize(bind, controller, logger)
|
125
|
+
@assets = {}
|
126
|
+
@bind = bind
|
127
|
+
@controller = controller
|
128
|
+
@logger = logger
|
129
|
+
end
|
45
130
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
131
|
+
def javascripts(*params, &block)
|
132
|
+
add_assets :javascripts, params, &block
|
133
|
+
end
|
134
|
+
|
135
|
+
def stylesheets(*params, &block)
|
136
|
+
add_assets :stylesheets, params, &block
|
137
|
+
end
|
138
|
+
|
139
|
+
def templates(*params, &block)
|
140
|
+
add_assets :templates, params, &block
|
55
141
|
end
|
56
142
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
143
|
+
private
|
144
|
+
|
145
|
+
def add_assets(type, params, &block)
|
146
|
+
options = params.extract_options! unless type == :templates
|
147
|
+
capture = block_to_string &block
|
148
|
+
asset = delete_if_empty(:options => options, :params => params, :capture => capture)
|
149
|
+
if asset.empty?
|
150
|
+
remove_dups :javascripts, :params
|
151
|
+
remove_dups :stylesheets, :params, :capture
|
152
|
+
remove_dups :templates, :params, :capture
|
153
|
+
captures = []
|
154
|
+
tags = []
|
155
|
+
@assets[type].each do |item|
|
156
|
+
@capture = item[:capture]
|
157
|
+
@params = item[:params]
|
72
158
|
case type
|
73
159
|
when :javascripts
|
74
|
-
|
160
|
+
captures.push(@capture) if @capture
|
161
|
+
tags.push(eval("javascript_include_tag *[ @assets.params, @assets.options ].flatten.compact", @bind)) if @params
|
75
162
|
when :stylesheets
|
76
|
-
|
163
|
+
captures.push(@capture) if @capture
|
164
|
+
tags.push(eval("stylesheet_link_tag *[ @assets.params, @assets.options ].flatten.compact", @bind)) if @params
|
77
165
|
when :templates
|
78
|
-
textarea_template
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
when :javascripts
|
83
|
-
js.push(item) unless item.blank?
|
84
|
-
nil
|
85
|
-
else
|
86
|
-
item
|
166
|
+
captures.push(textarea_template(@params.pop.merge(:body => @capture))) if @capture
|
167
|
+
@params.each do |options|
|
168
|
+
captures << textarea_template(options)
|
169
|
+
end
|
87
170
|
end
|
88
171
|
end
|
89
|
-
|
90
|
-
|
91
|
-
|
172
|
+
case type
|
173
|
+
when :javascripts
|
174
|
+
tags.join("\n") + "\n<script type='text/javascript'>\n#{captures.join "\n"}\n</script>"
|
175
|
+
when :stylesheets
|
176
|
+
tags.join("\n") + "\n<style type='text/css'>\n#{captures.join "\n"}\n</style>"
|
177
|
+
when :templates
|
178
|
+
captures.uniq.join "\n"
|
179
|
+
end
|
92
180
|
else
|
93
|
-
assets
|
181
|
+
@assets[type] ||= []
|
182
|
+
@assets[type].push asset
|
94
183
|
end
|
95
184
|
end
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
185
|
+
|
186
|
+
def block_to_string(&block)
|
187
|
+
return nil unless block
|
188
|
+
@block = block
|
189
|
+
eval "capture(&@assets.block)", @bind
|
190
|
+
end
|
191
|
+
|
192
|
+
def delete_if_empty(hash)
|
193
|
+
list = []
|
194
|
+
hash.each { |key, value| list.push(key) if !value || value.empty? || value.blank? }
|
195
|
+
list.each { |key| hash.delete key }
|
196
|
+
hash
|
197
|
+
end
|
198
|
+
|
199
|
+
def remove_dups(asset_type, *types)
|
200
|
+
asset = @assets[asset_type]
|
201
|
+
types.each do |type|
|
202
|
+
list = []
|
203
|
+
asset.each do |a|
|
204
|
+
if list.include?(a[type])
|
205
|
+
a.delete type
|
206
|
+
else
|
207
|
+
list << a[type]
|
208
|
+
end
|
109
209
|
end
|
210
|
+
end if asset
|
211
|
+
end
|
212
|
+
|
213
|
+
def textarea_template(options)
|
214
|
+
id = 'template' + (options[:id] ? "_#{options[:id]}" : '')
|
215
|
+
if options[:body]
|
216
|
+
body = options[:body]
|
217
|
+
elsif options[:partial]
|
218
|
+
body = @controller.render_to_string :partial => options[:partial], :locals => options[:locals]
|
110
219
|
end
|
220
|
+
return nil unless body
|
221
|
+
"<textarea id='#{id}' style='display:none'>\n#{body}\n</textarea>"
|
111
222
|
end
|
112
223
|
end
|
113
|
-
|
114
224
|
end
|
data/lib/rails_widget/widget.rb
CHANGED
@@ -1,248 +1,169 @@
|
|
1
|
-
module
|
1
|
+
module RailsWidget
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
javascripts *(@layout_happened ? [ :layout => true ] : []) do
|
19
|
-
js
|
20
|
-
end
|
21
|
-
partial
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def require_widget(*path)
|
27
|
-
widgets, options = widget_instances path
|
28
|
-
widgets.each do |w|
|
29
|
-
w.copy_assets
|
30
|
-
js = w.helper_targets :javascripts
|
31
|
-
css = w.helper_targets :stylesheets
|
32
|
-
javascripts *(js + [ :cache => w.cache, :layout => @layout_happened ]) unless js.empty?
|
33
|
-
stylesheets *(css + [ :cache => w.cache, :layout => @layout_happened ]) unless css.empty?
|
34
|
-
templates *(w.assets[:templates].collect do |t|
|
35
|
-
[ File.basename(t), t, options.merge(:options => options) ]
|
36
|
-
end) unless w.assets[:templates].empty?
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def widget_flash_path(*path)
|
41
|
-
flash = path.pop
|
42
|
-
"/flash/widgets/#{path.join('/')}/#{flash}"
|
43
|
-
end
|
44
|
-
|
45
|
-
def widget_image(*path)
|
46
|
-
options = path.extract_options!
|
47
|
-
image = path.pop
|
48
|
-
image_tag "widgets/#{path.join('/')}/#{image}", options
|
49
|
-
end
|
50
|
-
|
51
|
-
def widget_image_path(*path)
|
52
|
-
image = path.pop
|
53
|
-
"/images/widgets/#{path.join('/')}/#{image}"
|
54
|
-
end
|
55
|
-
|
56
|
-
def widget_partial(*path)
|
57
|
-
options = path.extract_options!
|
58
|
-
partial = path.pop
|
59
|
-
path << options
|
60
|
-
widgets, options = widget_instances path
|
61
|
-
options = {
|
62
|
-
:locals => options.merge(:options => options),
|
63
|
-
:partial => "#{path.join('/')}/partials/#{partial}"
|
64
|
-
}
|
65
|
-
render options
|
66
|
-
end
|
67
|
-
|
68
|
-
def widget_instances(path)
|
69
|
-
@widgets ||= Widgets.new binding, controller, logger
|
70
|
-
options = path.extract_options!
|
71
|
-
@widgets.build path, options
|
72
|
-
end
|
73
|
-
|
74
|
-
class Widgets
|
75
|
-
attr :widgets, true
|
76
|
-
|
77
|
-
def initialize(bind, controller, logger)
|
78
|
-
@bind = bind
|
3
|
+
# Stores information about a widget and renders assets to <tt>public/</tt> when necessary.
|
4
|
+
#
|
5
|
+
class Widget
|
6
|
+
attr :assets, true # Paths for each ASSET_TYPE
|
7
|
+
attr :cache, true # Cache path for Rails asset helpers
|
8
|
+
attr :options, true # Options hash from options.rb
|
9
|
+
attr :path, true # Path to widget
|
10
|
+
|
11
|
+
ASSET_TYPES = [ :flash, :images, :javascripts, :stylesheets, :templates, :init_css, :init_js, :init_partials ]
|
12
|
+
|
13
|
+
# Calls <tt>update_options</tt> and <tt>update_asset</tt> for each <tt>ASSET_TYPE</tt>.
|
14
|
+
#
|
15
|
+
def initialize(path, bind, controller, logger)
|
16
|
+
@path = path
|
17
|
+
@bind = bind
|
79
18
|
@controller = controller
|
80
|
-
@logger
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
@widgets[r]
|
19
|
+
@logger = logger
|
20
|
+
|
21
|
+
@assets = {}
|
22
|
+
@options = {}
|
23
|
+
@rendered = {}
|
24
|
+
@targeted = {}
|
25
|
+
|
26
|
+
update_options
|
27
|
+
ASSET_TYPES.each do |type|
|
28
|
+
update_asset type
|
91
29
|
end
|
92
|
-
[ widgets, opts.merge(options) ]
|
93
30
|
end
|
94
31
|
|
95
|
-
|
96
|
-
|
97
|
-
def
|
98
|
-
|
99
|
-
last = paths.length - 1
|
100
|
-
paths.each_index do |x|
|
101
|
-
if x != 0 && File.exists?("app/widgets/#{paths[x]}")
|
102
|
-
ordered << related_paths(paths[x..last])
|
103
|
-
end
|
104
|
-
path = paths[0..x].join '/'
|
105
|
-
if File.exists?("app/widgets/#{path}")
|
106
|
-
ordered << path
|
107
|
-
end
|
108
|
-
end
|
109
|
-
ordered.flatten
|
32
|
+
# Returns a cache path suitable for Rails asset helpers.
|
33
|
+
#
|
34
|
+
def cache
|
35
|
+
'cache/' + (@path.empty? ? 'base' : @path.gsub('/', '_'))
|
110
36
|
end
|
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
|
-
@assets.each do |key, value|
|
138
|
-
from, to = to_path key
|
139
|
-
value.each do |asset|
|
140
|
-
base = File.basename asset
|
141
|
-
f = [ from, base ].join '/'
|
142
|
-
t = [ to, base ].join '/'
|
143
|
-
t.gsub!('/stylesheets/', '/stylesheets/sass/') if t.include?('.sass')
|
144
|
-
next unless needs_update?(f, t)
|
145
|
-
case key
|
146
|
-
when :flash, :images
|
147
|
-
FileUtils.mkdir_p to
|
148
|
-
FileUtils.copy f, t
|
149
|
-
when :javascripts, :stylesheets
|
150
|
-
FileUtils.mkdir_p File.dirname(t)
|
151
|
-
File.open t, 'w' do |file|
|
152
|
-
file.write @controller.render_to_string(:file => f, :locals => @options.merge(:options => @options))
|
153
|
-
end
|
37
|
+
|
38
|
+
# Copies widget images to <tt>public/images/widgets</tt>.
|
39
|
+
#
|
40
|
+
# Copies widget flash files to <tt>public/flash/widgets</tt>.
|
41
|
+
#
|
42
|
+
# Renders javascripts to <tt>public/javascripts/widgets</tt>.
|
43
|
+
#
|
44
|
+
# Renders stylesheets to <tt>public/stylesheets/widgets</tt>.
|
45
|
+
#
|
46
|
+
def copy_assets
|
47
|
+
@assets.each do |key, value|
|
48
|
+
from, to = to_path key
|
49
|
+
value.each do |asset|
|
50
|
+
base = File.basename asset
|
51
|
+
f = [ from, base ].join '/'
|
52
|
+
t = [ to, base ].join '/'
|
53
|
+
t.gsub!('/stylesheets/', '/stylesheets/sass/') if t.include?('.sass')
|
54
|
+
next unless needs_update?(f, t)
|
55
|
+
case key
|
56
|
+
when :flash, :images
|
57
|
+
FileUtils.mkdir_p to
|
58
|
+
FileUtils.cp_r f, t
|
59
|
+
when :javascripts, :stylesheets
|
60
|
+
FileUtils.mkdir_p File.dirname(t)
|
61
|
+
File.open t, 'w' do |file|
|
62
|
+
file.write @controller.render_to_string(:file => f, :locals => @options.merge(:options => @options))
|
154
63
|
end
|
155
64
|
end
|
156
65
|
end
|
157
66
|
end
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
end
|
174
|
-
else @assets[type]
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns asset paths to be included via the Assets helpers for a particular <tt>ASSET_TYPE</tt>.
|
70
|
+
#
|
71
|
+
# See <tt>add_static_assets (Widgets)</tt>.
|
72
|
+
#
|
73
|
+
def asset_paths(type)
|
74
|
+
return [] if @targeted[type]
|
75
|
+
@targeted[type] = true
|
76
|
+
|
77
|
+
from, to = to_path type
|
78
|
+
case type
|
79
|
+
when :javascripts
|
80
|
+
@assets[type].collect do |asset|
|
81
|
+
[ to.split('javascripts/')[1], File.basename(asset, '.js') ].join '/'
|
175
82
|
end
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
return nil if @rendered[type][options[:id]]
|
181
|
-
@rendered[type][options[:id]] = true
|
182
|
-
|
183
|
-
@assets["init_#{type}".intern].collect do |f|
|
184
|
-
@controller.render_to_string :file => f, :locals => options.merge(:options => options)
|
185
|
-
end.join("\n")
|
186
|
-
end
|
187
|
-
|
188
|
-
private
|
189
|
-
|
190
|
-
def cache_name
|
191
|
-
'cache/' + (@path.empty? ? 'base' : @path.gsub('/', '_'))
|
192
|
-
end
|
193
|
-
|
194
|
-
def filename_to_partial(file, remove=nil)
|
195
|
-
base = File.basename file
|
196
|
-
dir = File.dirname file
|
197
|
-
file = [ dir, (base[0..0] == '_' ? base[1..-1] : base ).split('.')[0..-2].join('.') ].join '/'
|
198
|
-
if remove
|
199
|
-
if remove.respond_to?(:pop)
|
200
|
-
remove.each { |r| file.gsub! r, '' }
|
201
|
-
else
|
202
|
-
file.gsub! remove, ''
|
203
|
-
end
|
83
|
+
when :stylesheets
|
84
|
+
@assets[type].collect do |asset|
|
85
|
+
sass = asset.include? '.sass'
|
86
|
+
[ to.split('stylesheets/')[1], File.basename(asset, sass ? '.sass' : '.css') ].join '/'
|
204
87
|
end
|
205
|
-
|
88
|
+
else @assets[type]
|
206
89
|
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Renders and returns the init file for a particular <tt>ASSET_TYPE</tt>.
|
93
|
+
#
|
94
|
+
# The render will not occur if it has already happened with the same <tt>:id</tt> option.
|
95
|
+
#
|
96
|
+
def render_init(type, options=@options)
|
97
|
+
@rendered[type] ||= {}
|
98
|
+
#return nil if @rendered[type][options[:id]]
|
99
|
+
@rendered[type][options[:id]] = true
|
207
100
|
|
208
|
-
|
209
|
-
|
210
|
-
end
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
101
|
+
@assets["init_#{type}".intern].collect do |f|
|
102
|
+
@controller.render_to_string :file => f, :locals => options.merge(:options => options)
|
103
|
+
end.join("\n")
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Converts a full file name to a path that can be used by <tt>render :partial</tt>.
|
109
|
+
#
|
110
|
+
def filename_to_partial(file, remove=nil) #:doc:
|
111
|
+
base = File.basename file
|
112
|
+
dir = File.dirname file
|
113
|
+
file = [ dir, (base[0..0] == '_' ? base[1..-1] : base ).split('.')[0..-2].join('.') ].join '/'
|
114
|
+
if remove
|
115
|
+
if remove.respond_to?(:pop)
|
116
|
+
remove.each { |r| file.gsub! r, '' }
|
117
|
+
else
|
118
|
+
file.gsub! remove, ''
|
225
119
|
end
|
226
120
|
end
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
121
|
+
file
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns true if <tt>from</tt> is newer than <tt>to</tt> or <tt>to</tt> does not exist.
|
125
|
+
#
|
126
|
+
def needs_update?(from, to) #:doc:
|
127
|
+
File.exists?(to) ? File.mtime(from) > File.mtime(to) : true
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns a full path for the specified <tt>ASSET_TYPE</tt>.
|
131
|
+
#
|
132
|
+
def to_path(type, path=@path) #:doc:
|
133
|
+
slash = path.empty? ? '' : '/'
|
134
|
+
base = "app/widgets#{slash}#{path}"
|
135
|
+
case type
|
136
|
+
when :base: base
|
137
|
+
when :init_css: base + '/stylesheets/init'
|
138
|
+
when :init_js: base + '/javascripts/init'
|
139
|
+
when :init_partials: base + '/partials/_init'
|
140
|
+
when :options: base + '/options.rb'
|
141
|
+
when :templates: base + '/templates'
|
142
|
+
when :flash: [ base + '/flash', "public/flash/widgets" + slash + path ]
|
143
|
+
when :images: [ base + '/images', "public/images/widgets" + slash + path ]
|
144
|
+
when :javascripts: [ base + '/javascripts', "public/javascripts/widgets" + slash + path ]
|
145
|
+
when :stylesheets: [ base + '/stylesheets', "public/stylesheets/widgets" + slash + path ]
|
237
146
|
end
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
147
|
+
end
|
148
|
+
|
149
|
+
# Updates <tt>@assets[type]</tt> with an array of paths for the specified <tt>ASSET_TYPE</tt>.
|
150
|
+
#
|
151
|
+
def update_asset(type) #:doc:
|
152
|
+
@assets[type] ||= []
|
153
|
+
from = to_path type
|
154
|
+
from = from[0] if from.respond_to?(:pop)
|
155
|
+
from = File.directory?(from) ? "#{from}/*" : "#{from}.*"
|
156
|
+
Dir[from].sort.each do |f|
|
157
|
+
next if (type == :javascripts || type == :stylesheets) && File.basename(f)[0..3] == 'init'
|
158
|
+
@assets[type] << (type == :templates ? filename_to_partial(f, 'app/widgets/') : f)
|
245
159
|
end
|
246
160
|
end
|
161
|
+
|
162
|
+
# Assigns <tt>@options</tt> to the hash in <tt>options.rb</tt> (if it exists).
|
163
|
+
#
|
164
|
+
def update_options(path=@path, empty=false) #:doc:
|
165
|
+
path = to_path :options, path
|
166
|
+
@options = File.exists?(path) ? eval(File.read(path), @bind) : {}
|
167
|
+
end
|
247
168
|
end
|
248
169
|
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
module RailsWidget
|
2
|
+
|
3
|
+
# See <tt>RailsWidget</tt>.
|
4
|
+
#
|
5
|
+
def widget(*path)
|
6
|
+
@assets ||= Assets.new binding, controller, logger
|
7
|
+
@widgets ||= Widgets.new @assets, binding, controller, logger
|
8
|
+
options = path.extract_options!
|
9
|
+
@widgets.build path, options
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns a path for a flash asset.
|
13
|
+
#
|
14
|
+
# ==== Example
|
15
|
+
# <%= flash_path :some, :widget, 'flash.swf' %>
|
16
|
+
# # => 'app/widgets/some/widget/flash/flash.swf'
|
17
|
+
#
|
18
|
+
def flash_path(*path)
|
19
|
+
flash = path.pop
|
20
|
+
"/flash/widgets/#{path.join('/')}/#{flash}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns an image tag for a image asset.
|
24
|
+
#
|
25
|
+
# ==== Example
|
26
|
+
# <%= image :some, :widget, 'image.png', :border => 0 %>
|
27
|
+
# # => '<img src="app/widgets/some/widget/images/image.png" border=0 />'
|
28
|
+
#
|
29
|
+
def image(*path)
|
30
|
+
options = path.extract_options!
|
31
|
+
image = path.pop
|
32
|
+
image_tag "widgets/#{path.join('/')}/#{image}", options
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns an image path for a image asset.
|
36
|
+
#
|
37
|
+
# ==== Example
|
38
|
+
# <%= image_path :some, :widget, 'image.png' %>
|
39
|
+
# # => 'app/widgets/some/widget/images/image.png'
|
40
|
+
#
|
41
|
+
def image_path(*path)
|
42
|
+
image = path.pop
|
43
|
+
"/images/widgets/#{path.join('/')}/#{image}"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Renders a partial asset.
|
47
|
+
#
|
48
|
+
# ==== Example
|
49
|
+
# <%= partial :some, :widget, 'partial', :locals => { :x => true } %>
|
50
|
+
# # => render :partial => 'app/widgets/some/widget/partials/partial', :locals => { :x => true }
|
51
|
+
#
|
52
|
+
def partial(*path)
|
53
|
+
options = path.extract_options!
|
54
|
+
partial = path.pop
|
55
|
+
path << options
|
56
|
+
widgets, options = widget_instances path
|
57
|
+
options = {
|
58
|
+
:locals => options.merge(:options => options),
|
59
|
+
:partial => "#{path.join('/')}/partials/#{partial}"
|
60
|
+
}
|
61
|
+
render options
|
62
|
+
end
|
63
|
+
|
64
|
+
# Creates and recycles instances of the Widget class.
|
65
|
+
#
|
66
|
+
class Widgets
|
67
|
+
attr :widgets, true
|
68
|
+
|
69
|
+
# Should be called from a helper. See <tt>widget (RailsWidget)</tt>.
|
70
|
+
#
|
71
|
+
# ==== Example
|
72
|
+
# w = Widgets.new Assets.new(binding, controller), binding, controller, logger
|
73
|
+
#
|
74
|
+
def initialize(assets, bind, controller, logger)
|
75
|
+
@assets = assets
|
76
|
+
@bind = bind
|
77
|
+
@controller = controller
|
78
|
+
@logger = logger
|
79
|
+
@widgets = {}
|
80
|
+
build
|
81
|
+
end
|
82
|
+
|
83
|
+
# See <tt>widget (RailsWidget)</tt>.
|
84
|
+
#
|
85
|
+
def build(path=[''], options={})
|
86
|
+
widgets, opts = instanciate path
|
87
|
+
options = opts.merge options # Merge the options parameter (highest precedence)
|
88
|
+
add_static_assets widgets, options
|
89
|
+
return_init_assets widgets, options # Returns the init partial to <tt>widget (RailsWidget)</tt>
|
90
|
+
end
|
91
|
+
|
92
|
+
# Creates Widget instances for the widget path and its <tt>related_paths</tt> if they do not already exist.
|
93
|
+
#
|
94
|
+
# Returns an array of widget instances and merged <tt>options.rb</tt> hashes.
|
95
|
+
#
|
96
|
+
# ==== Example
|
97
|
+
# w.build([ :some, :widget ], { :option1 => true })
|
98
|
+
# # => [ #<Widget>, #<Widget>, #<Widget> ], { :option1 => true, :option2 => true }
|
99
|
+
#
|
100
|
+
# (See the <tt>related_paths</tt> example for context.)
|
101
|
+
#
|
102
|
+
def instanciate(path)
|
103
|
+
opts = {}
|
104
|
+
widgets = related_paths(path).collect do |r|
|
105
|
+
@widgets[r] ||= Widget.new r, @bind, @controller, @logger
|
106
|
+
opts.merge! @widgets[r].options
|
107
|
+
@widgets[r]
|
108
|
+
end
|
109
|
+
[ widgets, opts ]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Calls <tt>copy_assets (Widget)</tt> for a number of <tt>Widget</tt> instances.
|
113
|
+
#
|
114
|
+
# Also adds static (non-init) assets to the layout via the Assets helpers.
|
115
|
+
#
|
116
|
+
def add_static_assets(widgets, options)
|
117
|
+
widgets.each do |w|
|
118
|
+
w.copy_assets
|
119
|
+
js = w.asset_paths :javascripts
|
120
|
+
css = w.asset_paths :stylesheets
|
121
|
+
@assets.javascripts *(js + [ :cache => w.cache ]) unless js.empty?
|
122
|
+
@assets.stylesheets *(css + [ :cache => w.cache ]) unless css.empty?
|
123
|
+
@assets.templates *(w.assets[:templates].collect do |t|
|
124
|
+
{ :id => File.basename(t), :partial => t, :locals => options.merge(:options => options) }
|
125
|
+
end) unless w.assets[:templates].empty?
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Renders and returns the init partial (<tt>partials/_init.*</tt>) for a number of <tt>Widget</tt> instances.
|
130
|
+
#
|
131
|
+
# The <tt>:include_js => true</tt> option appends <tt>javascripts/init.js</tt> in <script> tags.
|
132
|
+
# Use this option when rendering a widget in an Ajax response.
|
133
|
+
#
|
134
|
+
def return_init_assets(widgets, options)
|
135
|
+
# Render partials/_init.* (options[:include_js] will render javascripts/init.js in <script> tags)
|
136
|
+
widgets.collect do |w|
|
137
|
+
# We want widgets rendered from the partial to include first
|
138
|
+
partial = w.render_init :partials, options
|
139
|
+
css = w.render_init :css, options
|
140
|
+
js = w.render_init :js, options
|
141
|
+
if options[:include_js] && js && !js.empty?
|
142
|
+
partial + "\n<script type='text/javascript'>\n#{js}\n</script>"
|
143
|
+
else
|
144
|
+
@assets.stylesheets do
|
145
|
+
css
|
146
|
+
end unless css.empty?
|
147
|
+
@assets.javascripts do
|
148
|
+
js
|
149
|
+
end unless js.empty?
|
150
|
+
partial
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
# Returns an array of related paths based on a single widget path.
|
158
|
+
#
|
159
|
+
# ==== Example
|
160
|
+
# related_paths([ :some, :widget ])
|
161
|
+
# # => [ 'some', 'widget', 'some/widget' ]
|
162
|
+
#
|
163
|
+
# Options are merged based on the order of the array that <tt>related_paths</tt> returns:
|
164
|
+
# app/widgets/some/options.rb # { :option1 => true, :option2 => false }
|
165
|
+
# app/widgets/widget/options.rb # { :option1 => false }
|
166
|
+
# app/widgets/some/widget/options.rb # { :option2 => true }
|
167
|
+
#
|
168
|
+
# Sequentially merging the options in this example produces <tt>{ :option1 => false, :option2 => true }</tt>.
|
169
|
+
#
|
170
|
+
# Assets are also included and rendered (init files) in the order of the <tt>related_paths</tt> array.
|
171
|
+
#
|
172
|
+
def related_paths(paths)
|
173
|
+
ordered = []
|
174
|
+
last = paths.length - 1
|
175
|
+
paths.each_index do |x|
|
176
|
+
if x != 0 && File.exists?("app/widgets/#{paths[x]}")
|
177
|
+
ordered << related_paths(paths[x..last])
|
178
|
+
end
|
179
|
+
path = paths[0..x].join '/'
|
180
|
+
if File.exists?("app/widgets/#{path}")
|
181
|
+
ordered << path
|
182
|
+
end
|
183
|
+
end
|
184
|
+
ordered.flatten
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: winton-rails_widget
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: "1.1"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Winton Welsh
|
@@ -9,11 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-08
|
12
|
+
date: 2008-11-08 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description:
|
16
|
+
description: Allows you to group your client-side assets into distributable widgets
|
17
17
|
email: mail@wintoni.us
|
18
18
|
executables: []
|
19
19
|
|
@@ -25,12 +25,12 @@ files:
|
|
25
25
|
- init.rb
|
26
26
|
- lib/rails_widget.rb
|
27
27
|
- lib/rails_widget
|
28
|
+
- lib/rails_widget/widgets.rb
|
28
29
|
- lib/rails_widget/assets.rb
|
29
30
|
- lib/rails_widget/widget.rb
|
30
31
|
- MIT-LICENSE
|
31
32
|
- README.markdown
|
32
|
-
|
33
|
-
has_rdoc: false
|
33
|
+
has_rdoc: true
|
34
34
|
homepage: http://github.com/winton/rails_widget
|
35
35
|
post_install_message:
|
36
36
|
rdoc_options: []
|
@@ -55,6 +55,6 @@ rubyforge_project:
|
|
55
55
|
rubygems_version: 1.2.0
|
56
56
|
signing_key:
|
57
57
|
specification_version: 2
|
58
|
-
summary:
|
58
|
+
summary: Allows you to group your client-side assets into distributable widgets
|
59
59
|
test_files: []
|
60
60
|
|
data/tasks/rails_widget.rake
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
desc 'Updates app/widgets assets'
|
2
|
-
task :widgets => [ 'widgets:javascripts', 'widgets:stylesheets' ]
|
3
|
-
|
4
|
-
namespace :widgets do
|
5
|
-
desc 'Updates app/widgets/javascripts'
|
6
|
-
task :javascripts do
|
7
|
-
rails_widget_resource 'widgets/javascripts', 'app/widgets/javascripts'
|
8
|
-
end
|
9
|
-
|
10
|
-
desc 'Updates app/widgets/stylesheets'
|
11
|
-
task :stylesheets do
|
12
|
-
rails_widget_resource 'widgets/stylesheets', 'app/widgets/stylesheets'
|
13
|
-
end
|
14
|
-
|
15
|
-
def rails_widget_resource(type, to, reverse=false)
|
16
|
-
from = "#{File.dirname(__FILE__)}/../resources/#{type}"
|
17
|
-
from, to = to, from if reverse
|
18
|
-
puts "=> Removing old #{type}..."
|
19
|
-
FileUtils.remove_dir to, true
|
20
|
-
FileUtils.mkdir_p to
|
21
|
-
puts "=> Copying #{type}..."
|
22
|
-
Dir["#{from}/*"].each do |f|
|
23
|
-
if File.directory? f
|
24
|
-
FileUtils.mkdir_p "#{to}/#{File.basename(f)}"
|
25
|
-
FileUtils.cp_r f, to
|
26
|
-
else
|
27
|
-
FileUtils.cp f, to
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|