winton-rails_widget 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.markdown +8 -0
- data/init.rb +1 -0
- data/lib/rails_widget/assets.rb +114 -0
- data/lib/rails_widget/widget.rb +248 -0
- data/lib/rails_widget.rb +7 -0
- data/tasks/rails_widget.rake +31 -0
- metadata +1 -1
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Winton Welsh <mail@wintoni.us>
|
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.markdown
ADDED
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'rails_widget'
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module WidgetHelpers
|
2
|
+
|
3
|
+
def default_javascript
|
4
|
+
"#{params[:controller]}/#{params[:action]}"
|
5
|
+
end
|
6
|
+
|
7
|
+
def default_stylesheet
|
8
|
+
"#{params[:controller]}/#{params[:action]}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def javascripts(*paths, &block)
|
12
|
+
add_assets :javascripts, paths, &block
|
13
|
+
end
|
14
|
+
|
15
|
+
def stylesheets(*paths, &block)
|
16
|
+
add_assets :stylesheets, paths, &block
|
17
|
+
end
|
18
|
+
|
19
|
+
def templates(*paths, &block)
|
20
|
+
paths.each do |path|
|
21
|
+
add_assets :templates, path, &block
|
22
|
+
end
|
23
|
+
add_assets(:templates, paths, &block) if paths.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def textarea_template(id, path=nil, locals={})
|
27
|
+
controller.render_to_string(:partial => 'app_helpers/template/textarea', :locals => {
|
28
|
+
:id => id,
|
29
|
+
:body => controller.render_to_string(:partial => path, :locals => locals)
|
30
|
+
})
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def add_assets(type, paths, &block)
|
36
|
+
options = paths.extract_options!
|
37
|
+
paths.flatten! unless type == :templates
|
38
|
+
|
39
|
+
@assets ||= {}
|
40
|
+
@assets[type] ||= []
|
41
|
+
@layout_assets ||= {}
|
42
|
+
@layout_assets[type] ||= []
|
43
|
+
|
44
|
+
paths = nil if paths.empty?
|
45
|
+
|
46
|
+
if options[:layout]
|
47
|
+
options.delete :layout
|
48
|
+
paths.push(options) if paths
|
49
|
+
@layout_assets[type].push(paths ) if paths
|
50
|
+
@layout_assets[type].push(capture(&block)) if block
|
51
|
+
else
|
52
|
+
paths.push(options) if paths
|
53
|
+
@assets[type].push(paths ) if paths
|
54
|
+
@assets[type].push(capture(&block)) if block
|
55
|
+
end
|
56
|
+
|
57
|
+
if !paths && !block
|
58
|
+
#logger.info type.inspect
|
59
|
+
#logger.info 'LAYOUT ' + @layout_assets[type].inspect
|
60
|
+
#logger.info @assets[type].inspect
|
61
|
+
|
62
|
+
@assets[type] = @layout_assets[type] + @assets[type]
|
63
|
+
|
64
|
+
@assets[type].uniq!
|
65
|
+
remove_dups @assets[type]
|
66
|
+
@assets[type].collect! { |a| a[0].respond_to?(:keys) ? nil : a }
|
67
|
+
@assets[type].compact!
|
68
|
+
|
69
|
+
js = []
|
70
|
+
assets = @assets[type].collect do |item|
|
71
|
+
if item.respond_to?(:pop)
|
72
|
+
case type
|
73
|
+
when :javascripts
|
74
|
+
javascript_include_tag *item
|
75
|
+
when :stylesheets
|
76
|
+
stylesheet_link_tag *item
|
77
|
+
when :templates
|
78
|
+
textarea_template item[0], item[1], item[2]
|
79
|
+
end + "\n"
|
80
|
+
else
|
81
|
+
case type
|
82
|
+
when :javascripts
|
83
|
+
js.push(item) unless item.blank?
|
84
|
+
nil
|
85
|
+
else
|
86
|
+
item
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end.compact
|
90
|
+
if type == :javascripts
|
91
|
+
assets.join + "<script type='text/javascript'>\n#{js.join "\n"}\n</script>"
|
92
|
+
else
|
93
|
+
assets.join
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def remove_dups(arr, list=[])
|
99
|
+
arr.dup.each do |a|
|
100
|
+
if a.respond_to?(:keys)
|
101
|
+
next
|
102
|
+
elsif a.respond_to?(:pop)
|
103
|
+
remove_dups a, list
|
104
|
+
else
|
105
|
+
if list.include?(a) || a.blank?
|
106
|
+
arr.delete_at arr.rindex(a)
|
107
|
+
else
|
108
|
+
list << a
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
module WidgetHelpers
|
2
|
+
|
3
|
+
def widget_base
|
4
|
+
@layout_happened = true
|
5
|
+
require_widget ''
|
6
|
+
render_widget ''
|
7
|
+
end
|
8
|
+
|
9
|
+
def render_widget(*path)
|
10
|
+
widgets, options = widget_instances path
|
11
|
+
widgets.collect do |w|
|
12
|
+
# we want widgets rendered from the partial to include first
|
13
|
+
partial = w.render_init :partials, options
|
14
|
+
js = w.render_init :js, options
|
15
|
+
if options[:include_js] && js && !js.empty?
|
16
|
+
partial + "\n<script type='text/javascript'>\n#{js}\n</script>"
|
17
|
+
else
|
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
|
79
|
+
@controller = controller
|
80
|
+
@logger = logger
|
81
|
+
@widgets = {}
|
82
|
+
end
|
83
|
+
|
84
|
+
def build(path, options)
|
85
|
+
opts = {}
|
86
|
+
#@logger.info 'RELATED_PATHS ' + related_paths(path).inspect
|
87
|
+
widgets = related_paths(path).collect do |r|
|
88
|
+
@widgets[r] ||= Assets.new r, @bind, @controller, @logger
|
89
|
+
opts.merge! @widgets[r].options
|
90
|
+
@widgets[r]
|
91
|
+
end
|
92
|
+
[ widgets, opts.merge(options) ]
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def related_paths(paths)
|
98
|
+
ordered = []
|
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
|
110
|
+
end
|
111
|
+
|
112
|
+
class Assets
|
113
|
+
attr :assets, true
|
114
|
+
attr :cache, true
|
115
|
+
attr :options, true
|
116
|
+
attr :path, true
|
117
|
+
|
118
|
+
ASSET_TYPES = [ :flash, :images, :javascripts, :stylesheets, :templates, :init_js, :init_partials ]
|
119
|
+
|
120
|
+
def initialize(path, bind, controller, logger)
|
121
|
+
@bind = bind
|
122
|
+
@controller = controller
|
123
|
+
@logger = logger
|
124
|
+
@assets = {}
|
125
|
+
@options = {}
|
126
|
+
@rendered = {}
|
127
|
+
@targeted = {}
|
128
|
+
@path = path
|
129
|
+
@cache = cache_name
|
130
|
+
update_options
|
131
|
+
ASSET_TYPES.each do |type|
|
132
|
+
update_asset type
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def copy_assets
|
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
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def helper_targets(type)
|
160
|
+
return [] if @targeted[type]
|
161
|
+
@targeted[type] = true
|
162
|
+
|
163
|
+
from, to = to_path type
|
164
|
+
case type
|
165
|
+
when :javascripts
|
166
|
+
@assets[type].collect do |asset|
|
167
|
+
[ to.split('javascripts/')[1], File.basename(asset, '.js') ].join '/'
|
168
|
+
end
|
169
|
+
when :stylesheets
|
170
|
+
@assets[type].collect do |asset|
|
171
|
+
sass = asset.include? '.sass'
|
172
|
+
[ to.split('stylesheets/')[1], File.basename(asset, sass ? '.sass' : '.css') ].join '/'
|
173
|
+
end
|
174
|
+
else @assets[type]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def render_init(type, options=@options)
|
179
|
+
@rendered[type] ||= {}
|
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
|
204
|
+
end
|
205
|
+
file
|
206
|
+
end
|
207
|
+
|
208
|
+
def needs_update?(from, to)
|
209
|
+
File.exists?(to) ? File.mtime(from) > File.mtime(to) : true
|
210
|
+
end
|
211
|
+
|
212
|
+
def to_path(type, path=@path)
|
213
|
+
slash = path.empty? ? '' : '/'
|
214
|
+
base = "app/widgets#{slash}#{path}"
|
215
|
+
case type
|
216
|
+
when :base: base
|
217
|
+
when :init_js: base + '/javascripts/init'
|
218
|
+
when :init_partials: base + '/partials/_init'
|
219
|
+
when :options: base + '/options.rb'
|
220
|
+
when :templates: base + '/templates'
|
221
|
+
when :flash: [ base + '/flash', "public/flash/widgets" + slash + path ]
|
222
|
+
when :images: [ base + '/images', "public/images/widgets" + slash + path ]
|
223
|
+
when :javascripts: [ base + '/javascripts', "public/javascripts/widgets" + slash + path ]
|
224
|
+
when :stylesheets: [ base + '/stylesheets', "public/stylesheets/widgets" + slash + path ]
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def update_asset(type)
|
229
|
+
@assets[type] ||= []
|
230
|
+
from = to_path type
|
231
|
+
from = from[0] if from.respond_to?(:pop)
|
232
|
+
from = File.directory?(from) ? "#{from}/*" : "#{from}.*"
|
233
|
+
Dir[from].sort.each do |f|
|
234
|
+
next if type == :javascripts && File.basename(f) == 'init.js'
|
235
|
+
@assets[type] << (type == :templates ? filename_to_partial(f, 'app/widgets/') : f)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def update_options(path=@path, empty=false)
|
240
|
+
options = to_path :options, path
|
241
|
+
@options = (File.exists?(options) ? eval(File.read(options), @bind) : {}).merge(@options)
|
242
|
+
path = path.split('/')[0..-2]
|
243
|
+
# empty allows us to retrieve base directory's options
|
244
|
+
update_options(path.join('/'), path.empty?) unless empty
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
data/lib/rails_widget.rb
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Dir[File.expand_path('*/*.rb', File.dirname(__FILE__))].each do |f|
|
2
|
+
require [ File.dirname(f), File.basename(f, '.rb') ].join('/')
|
3
|
+
end
|
4
|
+
|
5
|
+
ActionView::Base.send :include, WidgetHelpers
|
6
|
+
ActionController::Base.send :include, WidgetHelpers
|
7
|
+
ActionController::Base.view_paths += [ RAILS_ROOT + '/app/widgets' ]
|
@@ -0,0 +1,31 @@
|
|
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
|