winton-rails_widget 1.0.1 → 1.0.2

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/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