Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 530 lines (461 sloc) 19.971 kb
5f4149e Mike Gunderloy Cleanup and reorganization
authored
1 require 'open-uri'
2 require 'yaml'
3
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
4 module Rails
5 class TemplateRunner
5f4149e Mike Gunderloy Cleanup and reorganization
authored
6
7 # Logging
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
8 # Turn on for noisy logging during template generation
6f78ddf Mike Gunderloy Turn off debug logging ...again...
authored
9 DEBUG_LOGGING = false
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
10
5f4149e Mike Gunderloy Cleanup and reorganization
authored
11 def debug_log(msg)
12 if DEBUG_LOGGING
13 log msg
14 end
15 end
16
17 # Accessors and Initialization
18 def current_app_name
19 current_app_name = File.basename(File.expand_path(root))
20 end
21
c655fac Mike Gunderloy Introduce template_identifier
authored
22 # generic accessors for any template
292efe7 Mike Gunderloy Expose gems and plugins hashes from the framework to individual template...
authored
23 attr_accessor :template_paths, :template_options, :template_identifier, :gems, :plugins
c655fac Mike Gunderloy Introduce template_identifier
authored
24 # specific accessors for current template
5f4149e Mike Gunderloy Cleanup and reorganization
authored
25 # TODO: This list should be data driven
26 attr_accessor :rails_branch, :database, :exception_handling, :monitoring, :branch_management, :rails_strategy, :link_rails_root,
27 :ie6_blocking, :javascript_library, :template_engine, :compass_css_framework, :design, :require_activation,
28 :mocking, :smtp_address, :smtp_domain, :smtp_username, :smtp_password, :capistrano_user, :capistrano_repo_host, :capistrano_production_host,
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
29 :capistrano_staging_host, :exceptional_api_key, :hoptoad_api_key, :newrelic_api_key, :notifier_email_from, :default_url_options_host,
a2c5012 Mike Gunderloy ActionMailer initialization updates
authored
30 :controller_type, :branches, :post_creation, :github_username, :github_token, :github_public, :admin_data_user_id, :admin_data_password,
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
31 :mail_in_development, :cap_gun_address, :cap_gun_port, :cap_gun_user_name, :cap_gun_password, :cap_gun_recipients
32
5f4149e Mike Gunderloy Cleanup and reorganization
authored
33 def add_template_path(path, placement = :prepend)
34 if placement == :prepend
35 @template_paths.unshift path
36 elsif placement == :append
37 @template_paths.push path
38 end
39 end
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
40
5f4149e Mike Gunderloy Cleanup and reorganization
authored
41 # TODO: List of attributes should be data driven
42 def init_template_framework(template, root)
43 @template_paths = [File.expand_path(File.dirname(template), File.join(root,'..'))]
9abafdf Mike Gunderloy Move and rename the template file
authored
44 @template_paths << File.join(File.expand_path(File.dirname(template), File.join(root,'..')), '../..')
c655fac Mike Gunderloy Introduce template_identifier
authored
45 @template_identifier = 'default'
5f4149e Mike Gunderloy Cleanup and reorganization
authored
46 end
47
c655fac Mike Gunderloy Introduce template_identifier
authored
48 def set_template_identifier(identifier)
49 @template_identifier = identifier
50 end
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
51
5f4149e Mike Gunderloy Cleanup and reorganization
authored
52 def load_options
53 # Option set-up
54 @template_options = load_template_config_file('config.yml')
55
56 @rails_branch = template_options["rails_branch"]
57 @rails_branch = "2-3-stable" if @rails_branch.nil?
58
59 @database = template_options["database"].nil? ? ask("Which database? postgresql (default), mysql, sqlite").downcase : template_options["database"]
60 @database = "postgresql" if @database.nil?
61
62 @exception_handling = template_options["exception_handling"].nil? ? ask("Which exception reporting? exceptional (default), hoptoad").downcase : template_options["exception_handling"]
63 @exception_handling = "exceptional" if @exception_handling.nil?
64
65 @monitoring = template_options["monitoring"].nil? ? ask("Which monitoring? new_relic (default), scout").downcase : template_options["monitoring"]
66 @monitoring = "new_relic" if @monitoring.nil?
67
68 @branch_management = template_options["branch_management"].nil? ? ask("Which branch management? piston (default), braid, git, none").downcase : template_options["branch_management"]
69 @branch_management = "piston" if @branch_management.nil?
70
71 @rails_strategy = template_options["rails_strategy"].nil? ? ask("Which Rails strategy? vendored (default), gem").downcase : template_options["rails_strategy"]
72 @rails_strategy = "vendored" if @rails_strategy.nil?
73
74 @link_rails_root = template_options["link_rails_root"]
75 @link_rails_root = "~/rails" if @link_rails_root.nil?
76
77 @ie6_blocking = template_options["ie6_blocking"].nil? ? ask("Which IE 6 blocking? none, light (default), ie6nomore").downcase : template_options["ie6_blocking"]
78 @ie6_blocking = "light" if @ie6_blocking.nil?
79
80 @javascript_library = template_options["javascript_library"].nil? ? ask("Which javascript library? prototype (default), jquery").downcase : template_options["javascript_library"]
81 @javascript_library = "prototype" if @javascript_library.nil?
82
83 @template_engine = template_options["template_engine"].nil? ? ask("Which template engine? erb (default), haml").downcase : template_options["template_engine"]
84 @template_engine = "erb" if @template_engine.nil?
85
86 @compass_css_framework = template_options["compass_css_framework"]
87 @compass_css_framework = "blueprint" if @compass_css_framework.nil?
88
89 @design = template_options["design"].nil? ? ask("Which design? none (default), bluetrip, compass").downcase : template_options["design"]
90 @design = "none" if @design.nil?
91
92 @require_activation = (template_options["require_activation"].to_s == "true")
93
94 @mocking = template_options["mocking"].nil? ? ask("Which mocking library? rr, mocha (default)").downcase : template_options["mocking"]
95 @mocking = "mocha" if @mocking.nil?
96
618fb8b Mike Gunderloy Make inherited_resources optional rather than default
authored
97 @controller_type = template_options["controller_type"].nil? ? ask("Which controller strategy? rails (default), inherited_resources").downcase : template_options["controller_type"]
98 @controller_type = "default" if @controller_type.nil? || @controller_type == 'rails'
99
a2c5012 Mike Gunderloy ActionMailer initialization updates
authored
100 @mail_in_development = template_options["mail_in_development"].nil? ? ask("Which development mail trap? inaction_mailer (default), mock_smtp").downcase : template_options["mail_in_development"]
101 @mail_in_development = "inaction_mailer" if @mail_in_development.nil?
102
6353745 Mike Gunderloy Add post-creation hook to build GitHub repository. See config.yml for re...
authored
103 @github_username = template_options["github_username"]
104 @github_token = template_options["github_token"]
105 @github_public = template_options["github_public"]
5f4149e Mike Gunderloy Cleanup and reorganization
authored
106 @smtp_address = template_options["smtp_address"]
107 @smtp_domain = template_options["smtp_domain"]
108 @smtp_username = template_options["smtp_username"]
109 @smtp_password = template_options["smtp_password"]
110 @capistrano_user = template_options["capistrano_user"]
111 @capistrano_repo_host = template_options["capistrano_repo_host"]
112 @capistrano_production_host = template_options["capistrano_production_host"]
113 @capistrano_staging_host = template_options["capistrano_staging_host"]
114 @exceptional_api_key = template_options["exceptional_api_key"]
115 @hoptoad_api_key = template_options["hoptoad_api_key"]
116 @newrelic_api_key = template_options["newrelic_api_key"]
117 @notifier_email_from = template_options["notifier_email_from"]
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
118 @default_url_options_host = template_options["default_url_options_host"]
147f387 Mike Gunderloy Adjust admin_data initializer to match current plugin
authored
119 @admin_data_user_id = template_options["admin_data_user_id"]
120 @admin_data_password = template_options["admin_data_password"]
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
121 @cap_gun_address = template_options["cap_gun_address"]
122 @cap_gun_port = template_options["cap_gun_port"]
123 @cap_gun_user_name = template_options["cap_gun_user_name"]
124 @cap_gun_password = template_options["cap_gun_password"]
125 @cap_gun_recipients = template_options["cap_gun_recipients"]
5f4149e Mike Gunderloy Cleanup and reorganization
authored
126
5e8d536 Mike Gunderloy Move branches setting to template framework, add post-creation setting
authored
127 @branches = template_options["git_branches"]
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
128
5e8d536 Mike Gunderloy Move branches setting to template framework, add post-creation setting
authored
129 @post_creation = template_options["post_creation"]
5f4149e Mike Gunderloy Cleanup and reorganization
authored
130 end
131
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
132 # File Management
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
133 def download(from, to = from.split("/").last)
134 #run "curl -s -L #{from} > #{to}"
135 file to, open(from).read
136 rescue
137 puts "Can't get #{from} - Internet down?"
138 exit!
139 end
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
140
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
141 # grab an arbitrary file from github
142 def file_from_repo(github_user, repo, sha, filename, to = filename)
143 download("http://github.com/#{github_user}/#{repo}/raw/#{sha}/#{filename}", to)
144 end
145
146 def load_from_file_in_template(file_name, parent_binding = nil, file_group = 'default', file_type = :pattern)
147 base_name = file_name.gsub(/^\./, '')
148 begin
149 if file_type == :config
150 contents = {}
151 else
152 contents = ''
153 end
154 paths = template_paths
155
156 paths.each do |template_path|
157 full_file_name = File.join(template_path, file_type.to_s.pluralize, file_group, base_name)
158 debug_log "Searching for #{full_file_name} ... "
159
160 next unless File.exists? full_file_name
161 debug_log "Found!"
162
163 if file_type == :config
164 contents = open(full_file_name) { |f| YAML.load(f) }
165 else
166 contents = open(full_file_name) { |f| f.read }
167 end
168 if contents && parent_binding
169 contents = eval("\"" + contents.gsub('"','\\"') + "\"", parent_binding)
170 end
171 # file loaded, stop searching
172 break if contents
173
174 end
175 contents
618fb8b Mike Gunderloy Make inherited_resources optional rather than default
authored
176 rescue => ex
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
177 debug_log "Error in load_from_file_in_template #{file_name}"
618fb8b Mike Gunderloy Make inherited_resources optional rather than default
authored
178 debug_log ex.message
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
179 end
180 end
181
182 # Load a snippet from a file
a2c5012 Mike Gunderloy ActionMailer initialization updates
authored
183 def load_snippet(snippet_name, snippet_group = "default", parent_binding = nil)
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
184 load_from_file_in_template(snippet_name, parent_binding, snippet_group, :snippet)
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
185 end
186
187 # Load a pattern from a file, potentially with string interpolation
188 def load_pattern(pattern_name, pattern_group = "default", parent_binding = nil)
189 load_from_file_in_template(pattern_name, parent_binding, pattern_group, :pattern)
190 end
191
192 # YAML.load a configuration from a file
c655fac Mike Gunderloy Introduce template_identifier
authored
193 def load_template_config_file(config_file_name, config_file_group = template_identifier)
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
194 load_from_file_in_template(config_file_name, nil, config_file_group, :config )
195 end
196
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
197 # SCM and Branch Management
5f4149e Mike Gunderloy Cleanup and reorganization
authored
198 def commit_state(comment)
199 git :add => "."
200 git :commit => "-am '#{comment}'"
201 end
202
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
203 # sudo gem install piston on your dev box before using these
204 # Piston locking support with git requires Piston 2.0.3+
205 # Piston branch management with git 1.6.3 requires Piston 2.0.5+
206
207 # Use Piston to install and lock a plugin:
208 # piston_plugin 'stuff', :git => 'git://github.com/whoever/stuff.git'
209 # Use Piston to install a plugin without locking:
210 # piston_plugin 'stuff', :git => 'git://github.com/whoever/stuff.git', :lock => false
211 def piston_plugin(name, options={})
212 lock = options.fetch(:lock, true)
213
214 if options[:git] || options[:svn]
215 in_root do
216 run("piston import #{options[:svn] || options[:git]} vendor/plugins/#{name}")
217 run("piston lock vendor/plugins/#{name}") if lock
218 commit_state("Added pistoned #{name}")
219 end
220 log "plugin installed #{'and locked ' if lock}with Piston:", name
221 else
222 log "! no git or svn provided for #{name}. skipping..."
223 end
224 end
225
226 # Use Piston to install and lock current Rails edge (master):
227 # piston_rails
228 # Use Piston to install but not lock current Rails edge (master):
229 # piston_rails :lock => false
230 # Use Piston to install and lock edge of a specific Rails branch:
231 # piston_rails :branch => "2-3-stable"
232 # Use Piston to install but not lock edge of a specific Rails branch:
233 # piston_rails, :branch => "2-3-stable", :lock => false
234 def piston_rails(options={})
235 lock = options.fetch(:lock, true)
236
237 if options[:branch]
238 in_root do
239 run("piston import --commit #{options[:branch]} git://github.com/rails/rails.git vendor/rails")
240 commit_state("Added pistoned Rails using the edge of the #{options[:branch]} branch")
241 if lock
242 run("piston lock vendor/rails")
243 commit_state("Locked pistoned rails")
244 end
245 end
246 else
247 in_root do
248 run("piston import git://github.com/rails/rails.git vendor/rails")
249 commit_state("Added pistoned Rails edge")
250 if lock
251 run("piston lock vendor/rails")
252 commit_state("Locked pistoned rails")
253 end
254 end
255 end
256
257 log "rails installed #{'and locked ' if lock}with Piston", options[:branch]
258 end
259
260 # braid support is experimental and largely untested
261 def braid_plugin(name, options={})
262 if options[:git]
263 in_root do
264 run("braid add -p #{options[:git]}")
265 commit_state("Added braided #{name}")
266 end
267 log "plugin installed with Braid:", name
268 else
269 log "! no git provided for #{name}. skipping..."
270 end
271 end
272
273 def braid_rails(options={})
274 if options[:branch]
275 log "! branch support for Braid is not yet implemented"
276 else
277 in_root do
278 run("braid add git://github.com/rails/rails.git vendor/rails")
279 log "rails installed with Braid"
280 end
281 end
282 end
283
284 # cloning rails is experimental and somewhat untested
285 def clone_rails(options={})
286 if options[:submodule]
287 in_root do
288 if options[:branch] && options[:branch] != "master"
289 git :submodule => "add git://github.com/rails/rails.git vendor/rails -b #{options[:branch]}"
290 else
291 git :submodule => "add git://github.com/rails/rails.git vendor/rails"
292 end
293 end
294 else
295 inside 'vendor' do
296 run('git clone git://github.com/rails/rails.git')
297 end
298 if options[:branch] && options[:branch] != "master"
299 inside 'vendor/rails' do
300 run("git branch --track #{options[:branch]} origin/#{options[:branch]}")
301 run("git checkout #{options[:branch]}")
302 end
303 end
304 end
305
306 log "rails installed #{'and submoduled ' if options[:submodule]}from GitHub", options[:branch]
307 end
308
496a6ba Mike Gunderloy Move a couple of things from the Lark template to the template framework
authored
309 # setup the specified branches in the git repo
310 def git_branch_setup
311 if !branches.nil?
312 default_branch = "master"
313 branches.each do |name, default|
314 if name != "master"
315 git :branch => name
316 default_branch = name if !default.nil?
317 end
318 end
319 git :checkout => default_branch if default_branch != "master"
320 log "set up branches #{branches.keys.join(', ')}"
321 end
322 end
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
323
5f4149e Mike Gunderloy Cleanup and reorganization
authored
324 # Rails Management
325
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
326 # update rails bits in application after vendoring a new copy of rails
327 # we need to do this the hard way because we want to overwrite without warning
328 # TODO: Can we introspect the actual rake:update task to get a current list of subtasks?
329 def update_app
330 in_root do
331 run("echo 'a' | rake rails:update:scripts")
332 run("echo 'a' | rake rails:update:javascripts")
333 run("echo 'a' | rake rails:update:configs")
334 run("echo 'a' | rake rails:update:application_controller")
335 end
336 end
337
5f4149e Mike Gunderloy Cleanup and reorganization
authored
338 # remove the prototype framework
339 def remove_prototype
340 run "rm public/javascripts/controls.js"
341 run "rm public/javascripts/dragdrop.js"
342 run "rm public/javascripts/effects.js"
343 run "rm public/javascripts/prototype.js"
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
344 end
345
346 def install_plugin (name, options)
347 case @branch_management
348 when 'none'
349 plugin name, options
350 when 'piston'
351 piston_plugin name, options
352 when 'braid'
353 braid_plugin name, options
354 when 'git'
355 plugin name, options.merge(:submodule => true)
356 end
357 end
358
359 def install_rails (options)
360 case @branch_management
361 when 'none'
362 clone_rails options
363 when 'piston'
364 piston_rails options
365 when 'braid'
366 braid_rails options
367 when 'git'
368 clone_rails options.merge(:submodule => true)
369 end
370 end
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
371
5f4149e Mike Gunderloy Cleanup and reorganization
authored
372 # Mocking generators
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
373
374 def generate_stub(object_name, method_name, return_value)
375 if @mocking == "rr"
376 "stub(#{object_name}).#{method_name}{ #{return_value} }"
377 elsif @mocking == "mocha"
378 "#{object_name}.stubs(:#{method_name}).returns(#{return_value})"
379 end
380 end
381
382 def generate_any_instance_stub(object_name, method_name, return_value)
383 if @mocking == "rr"
384 "stub.instance_of(#{object_name}).#{method_name}{ #{return_value} }"
385 elsif @mocking == "mocha"
386 "#{object_name}.any_instance.stubs(:#{method_name}).returns(#{return_value})"
387 end
388 end
389
390 def generate_expectation(object_name, method_name, parameter = nil)
391 if parameter
392 if @mocking == "rr"
393 "mock(#{object_name}).#{method_name}(#{parameter})"
394 elsif @mocking == "mocha"
395 "#{object_name}.expects(:#{method_name}).with(#{parameter})"
396 end
397 else
398 if @mocking == "rr"
399 "mock(#{object_name}).#{method_name}"
400 elsif @mocking == "mocha"
401 "#{object_name}.expects(:#{method_name})"
402 end
403 end
404 end
405
406 def generate_pure_stub(stub_name)
407 if @mocking == "rr"
408 "stub!('#{stub_name}')"
409 elsif @mocking == "mocha"
410 "stub('#{stub_name}')"
411 end
412 end
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
413
500fa93 Mike Gunderloy One-step Heroku app creation
authored
414 # Heroku management
496a6ba Mike Gunderloy Move a couple of things from the Lark template to the template framework
authored
415
416 # Run a command with the Heroku gem.
417 #
418 # ==== Examples
419 #
420 # heroku :create
421 # heroku :rake => "db:migrate"
422 #
423 def heroku(command = {})
424 in_root do
425 if command.is_a?(Symbol)
426 log 'running', "heroku #{command}"
427 run "heroku #{command}"
428 else
429 command.each do |command, options|
430 log 'running', "heroku #{command} #{options}"
431 run("heroku #{command} #{options}")
432 end
433 end
500fa93 Mike Gunderloy One-step Heroku app creation
authored
434 end
435 end
496a6ba Mike Gunderloy Move a couple of things from the Lark template to the template framework
authored
436
437 # post-creation hooks
438 def execute_post_creation_hooks
439 if !post_creation.nil?
440 post_creation.each do |name, options|
441 if name == 'heroku'
442 git :checkout => "master"
443 rake "gems:specify", :env => "production"
444 commit_state "added gem manifest"
445 heroku :create
446 git :push => "heroku master"
447 heroku :rake => "db:migrate"
448 heroku :restart
449 heroku :open
450 log "set up application at Heroku"
451 end
452 if name == 'github'
453 run "curl -F 'login=#{github_username}' -F 'token=#{github_token}' -F 'name=#{current_app_name}' -F 'public=#{github_public}' http://github.com/api/v2/json/repos/create"
454 git :remote => "add origin git@github.com:#{github_username}/#{current_app_name}.git"
455 git :push => "origin master"
456 if !branches.nil?
457 default_branch = "master"
458 branches.each do |name, default|
459 if name != "master"
460 git :push => "origin #{name}"
461 default_branch = name if !default.nil?
462 end
463 end
464 git :checkout => default_branch if default_branch != "master"
465 end
466 log "set up application at GitHub"
467 end
468 end
469 end
470 end
eaac9f0 Mike Gunderloy Move gem handling from template to framework.
authored
471
472 # Gem management
473 def install_gems
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
474 @gems = load_template_config_file('gems.yml')
292efe7 Mike Gunderloy Expose gems and plugins hashes from the framework to individual template...
authored
475 install_on_current(@gems)
476 add_to_project(@gems)
eaac9f0 Mike Gunderloy Move gem handling from template to framework.
authored
477 end
478
479 # If the geminstaller gem is present, use to to bootstrap the other
480 # needed gems on to the dev box so that rake succeeds
481 def install_on_current(gems)
482 begin
483 require 'geminstaller'
484 # Transform the gem array to the form that geminstaller wants to see
485 gem_array = []
486 gems.each do |name, value|
487 if value[:if].nil? || eval(value[:if])
488 h = Hash.new
489 h["name"] = name
490 if value[:options] && value[:options][:version]
491 h["version"] = value[:options][:version]
492 end
493 gem_array.push h
494 end
495 end
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
496
497 if !gem_array.empty?
eaac9f0 Mike Gunderloy Move gem handling from template to framework.
authored
498 geminstaller_hash = {"defaults"=>{"install_options"=>"--no-ri --no-rdoc"}, "gems"=> gem_array}
499 in_root do
500 File.open( 'geminstaller.yml', 'w' ) do |out|
501 YAML.dump( geminstaller_hash, out )
502 end
503 run 'geminstaller'
504 log "installed gems on current machine"
505 end
506 end
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
507
eaac9f0 Mike Gunderloy Move gem handling from template to framework.
authored
508 rescue LoadError
509 end
510 end
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
511
eaac9f0 Mike Gunderloy Move gem handling from template to framework.
authored
512 def add_to_project(gems)
513 gems.each do |name, value|
514 if value[:if].nil? || eval(value[:if])
515 gem name, value[:options]
516 end
517 end
518 end
e46497f Mike Gunderloy Move plugin load from template to framework
authored
519
520 #Plugin management
521 def install_plugins
c7ecfda Mike Gunderloy Add cap_gun for deployment notifications
authored
522 @plugins = load_template_config_file('plugins.yml')
292efe7 Mike Gunderloy Expose gems and plugins hashes from the framework to individual template...
authored
523 @plugins.each do |name, value|
e46497f Mike Gunderloy Move plugin load from template to framework
authored
524 if value[:if].nil? || eval(value[:if])
525 install_plugin name, value[:options]
526 end
527 end
528 end
d18f6a1 Mike Gunderloy Separate the template from the template framework
authored
529 end
530 end
Something went wrong with that request. Please try again.