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 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
@@ -0,0 +1,8 @@
1
+ Rails Widget
2
+ ============
3
+
4
+ A mini-framework for your client side Rails assets.
5
+
6
+ **This plugin is very beta. More details coming soon.**
7
+
8
+ ##### Copyright (c) 2008 [Winton Welsh](mailto:mail@wintoni.us), released under the MIT license
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
@@ -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
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.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Winton Welsh