PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/template_framework.rb

https://github.com/cwattengard/BigOldRailsTemplate
Ruby | 399 lines | 311 code | 49 blank | 39 comment | 55 complexity | 60ee7e022e65266815298f826d08dc37 MD5 | raw file
  1. require 'open-uri'
  2. require 'yaml'
  3. module Rails
  4. class TemplateRunner
  5. # Logging
  6. # Turn on for noisy logging during template generation
  7. DEBUG_LOGGING = false
  8. def debug_log(msg)
  9. if DEBUG_LOGGING
  10. log msg
  11. end
  12. end
  13. # Accessors and Initialization
  14. def current_app_name
  15. current_app_name = File.basename(File.expand_path(root))
  16. end
  17. # TODO: This list should be data driven
  18. attr_accessor :rails_branch, :database, :exception_handling, :monitoring, :branch_management, :rails_strategy, :link_rails_root,
  19. :ie6_blocking, :javascript_library, :template_engine, :compass_css_framework, :design, :require_activation,
  20. :mocking, :smtp_address, :smtp_domain, :smtp_username, :smtp_password, :capistrano_user, :capistrano_repo_host, :capistrano_production_host,
  21. :capistrano_staging_host, :exceptional_api_key, :hoptoad_api_key, :newrelic_api_key, :notifier_email_from, :default_url_options_host,
  22. :template_paths, :template_options, :controller_type, :branches, :post_creation
  23. def add_template_path(path, placement = :prepend)
  24. if placement == :prepend
  25. @template_paths.unshift path
  26. elsif placement == :append
  27. @template_paths.push path
  28. end
  29. end
  30. # TODO: List of attributes should be data driven
  31. def init_template_framework(template, root)
  32. @template_paths = [File.expand_path(File.dirname(template), File.join(root,'..'))]
  33. end
  34. def load_options
  35. # Option set-up
  36. @template_options = load_template_config_file('config.yml')
  37. @rails_branch = template_options["rails_branch"]
  38. @rails_branch = "2-3-stable" if @rails_branch.nil?
  39. @database = template_options["database"].nil? ? ask("Which database? postgresql (default), mysql, sqlite").downcase : template_options["database"]
  40. @database = "postgresql" if @database.nil?
  41. @exception_handling = template_options["exception_handling"].nil? ? ask("Which exception reporting? exceptional (default), hoptoad").downcase : template_options["exception_handling"]
  42. @exception_handling = "exceptional" if @exception_handling.nil?
  43. @monitoring = template_options["monitoring"].nil? ? ask("Which monitoring? new_relic (default), scout").downcase : template_options["monitoring"]
  44. @monitoring = "new_relic" if @monitoring.nil?
  45. @branch_management = template_options["branch_management"].nil? ? ask("Which branch management? piston (default), braid, git, none").downcase : template_options["branch_management"]
  46. @branch_management = "piston" if @branch_management.nil?
  47. @rails_strategy = template_options["rails_strategy"].nil? ? ask("Which Rails strategy? vendored (default), gem").downcase : template_options["rails_strategy"]
  48. @rails_strategy = "vendored" if @rails_strategy.nil?
  49. @link_rails_root = template_options["link_rails_root"]
  50. @link_rails_root = "~/rails" if @link_rails_root.nil?
  51. @ie6_blocking = template_options["ie6_blocking"].nil? ? ask("Which IE 6 blocking? none, light (default), ie6nomore").downcase : template_options["ie6_blocking"]
  52. @ie6_blocking = "light" if @ie6_blocking.nil?
  53. @javascript_library = template_options["javascript_library"].nil? ? ask("Which javascript library? prototype (default), jquery").downcase : template_options["javascript_library"]
  54. @javascript_library = "prototype" if @javascript_library.nil?
  55. @template_engine = template_options["template_engine"].nil? ? ask("Which template engine? erb (default), haml").downcase : template_options["template_engine"]
  56. @template_engine = "erb" if @template_engine.nil?
  57. @compass_css_framework = template_options["compass_css_framework"]
  58. @compass_css_framework = "blueprint" if @compass_css_framework.nil?
  59. @design = template_options["design"].nil? ? ask("Which design? none (default), bluetrip, compass").downcase : template_options["design"]
  60. @design = "none" if @design.nil?
  61. @require_activation = (template_options["require_activation"].to_s == "true")
  62. @mocking = template_options["mocking"].nil? ? ask("Which mocking library? rr, mocha (default)").downcase : template_options["mocking"]
  63. @mocking = "mocha" if @mocking.nil?
  64. @controller_type = template_options["controller_type"].nil? ? ask("Which controller strategy? rails (default), inherited_resources").downcase : template_options["controller_type"]
  65. @controller_type = "default" if @controller_type.nil? || @controller_type == 'rails'
  66. @smtp_address = template_options["smtp_address"]
  67. @smtp_domain = template_options["smtp_domain"]
  68. @smtp_username = template_options["smtp_username"]
  69. @smtp_password = template_options["smtp_password"]
  70. @capistrano_user = template_options["capistrano_user"]
  71. @capistrano_repo_host = template_options["capistrano_repo_host"]
  72. @capistrano_production_host = template_options["capistrano_production_host"]
  73. @capistrano_staging_host = template_options["capistrano_staging_host"]
  74. @exceptional_api_key = template_options["exceptional_api_key"]
  75. @hoptoad_api_key = template_options["hoptoad_api_key"]
  76. @newrelic_api_key = template_options["newrelic_api_key"]
  77. @notifier_email_from = template_options["notifier_email_from"]
  78. @default_url_options_host = template_options["default_url_options_host"]
  79. @branches = template_options["git_branches"]
  80. @post_creation = template_options["post_creation"]
  81. end
  82. # File Management
  83. def download(from, to = from.split("/").last)
  84. #run "curl -s -L #{from} > #{to}"
  85. file to, open(from).read
  86. rescue
  87. puts "Can't get #{from} - Internet down?"
  88. exit!
  89. end
  90. # grab an arbitrary file from github
  91. def file_from_repo(github_user, repo, sha, filename, to = filename)
  92. download("http://github.com/#{github_user}/#{repo}/raw/#{sha}/#{filename}", to)
  93. end
  94. def load_from_file_in_template(file_name, parent_binding = nil, file_group = 'default', file_type = :pattern)
  95. base_name = file_name.gsub(/^\./, '')
  96. begin
  97. if file_type == :config
  98. contents = {}
  99. else
  100. contents = ''
  101. end
  102. paths = template_paths
  103. paths.each do |template_path|
  104. full_file_name = File.join(template_path, file_type.to_s.pluralize, file_group, base_name)
  105. debug_log "Searching for #{full_file_name} ... "
  106. next unless File.exists? full_file_name
  107. debug_log "Found!"
  108. if file_type == :config
  109. contents = open(full_file_name) { |f| YAML.load(f) }
  110. else
  111. contents = open(full_file_name) { |f| f.read }
  112. end
  113. if contents && parent_binding
  114. contents = eval("\"" + contents.gsub('"','\\"') + "\"", parent_binding)
  115. end
  116. # file loaded, stop searching
  117. break if contents
  118. end
  119. contents
  120. rescue => ex
  121. debug_log "Error in load_from_file_in_template #{file_name}"
  122. debug_log ex.message
  123. end
  124. end
  125. # Load a snippet from a file
  126. def load_snippet(snippet_name, snippet_group = "default")
  127. load_from_file_in_template(snippet_name, nil, snippet_group, :snippet)
  128. end
  129. # Load a pattern from a file, potentially with string interpolation
  130. def load_pattern(pattern_name, pattern_group = "default", parent_binding = nil)
  131. load_from_file_in_template(pattern_name, parent_binding, pattern_group, :pattern)
  132. end
  133. # YAML.load a configuration from a file
  134. def load_template_config_file(config_file_name, config_file_group = "default")
  135. load_from_file_in_template(config_file_name, nil, config_file_group, :config )
  136. end
  137. # SCM and Branch Management
  138. def commit_state(comment)
  139. git :add => "."
  140. git :commit => "-am '#{comment}'"
  141. end
  142. # sudo gem install piston on your dev box before using these
  143. # Piston locking support with git requires Piston 2.0.3+
  144. # Piston branch management with git 1.6.3 requires Piston 2.0.5+
  145. # Use Piston to install and lock a plugin:
  146. # piston_plugin 'stuff', :git => 'git://github.com/whoever/stuff.git'
  147. # Use Piston to install a plugin without locking:
  148. # piston_plugin 'stuff', :git => 'git://github.com/whoever/stuff.git', :lock => false
  149. def piston_plugin(name, options={})
  150. lock = options.fetch(:lock, true)
  151. if options[:git] || options[:svn]
  152. in_root do
  153. run("piston import #{options[:svn] || options[:git]} vendor/plugins/#{name}")
  154. run("piston lock vendor/plugins/#{name}") if lock
  155. commit_state("Added pistoned #{name}")
  156. end
  157. log "plugin installed #{'and locked ' if lock}with Piston:", name
  158. else
  159. log "! no git or svn provided for #{name}. skipping..."
  160. end
  161. end
  162. # Use Piston to install and lock current Rails edge (master):
  163. # piston_rails
  164. # Use Piston to install but not lock current Rails edge (master):
  165. # piston_rails :lock => false
  166. # Use Piston to install and lock edge of a specific Rails branch:
  167. # piston_rails :branch => "2-3-stable"
  168. # Use Piston to install but not lock edge of a specific Rails branch:
  169. # piston_rails, :branch => "2-3-stable", :lock => false
  170. def piston_rails(options={})
  171. lock = options.fetch(:lock, true)
  172. if options[:branch]
  173. in_root do
  174. run("piston import --commit #{options[:branch]} git://github.com/rails/rails.git vendor/rails")
  175. commit_state("Added pistoned Rails using the edge of the #{options[:branch]} branch")
  176. if lock
  177. run("piston lock vendor/rails")
  178. commit_state("Locked pistoned rails")
  179. end
  180. end
  181. else
  182. in_root do
  183. run("piston import git://github.com/rails/rails.git vendor/rails")
  184. commit_state("Added pistoned Rails edge")
  185. if lock
  186. run("piston lock vendor/rails")
  187. commit_state("Locked pistoned rails")
  188. end
  189. end
  190. end
  191. log "rails installed #{'and locked ' if lock}with Piston", options[:branch]
  192. end
  193. # braid support is experimental and largely untested
  194. def braid_plugin(name, options={})
  195. if options[:git]
  196. in_root do
  197. run("braid add -p #{options[:git]}")
  198. commit_state("Added braided #{name}")
  199. end
  200. log "plugin installed with Braid:", name
  201. else
  202. log "! no git provided for #{name}. skipping..."
  203. end
  204. end
  205. def braid_rails(options={})
  206. if options[:branch]
  207. log "! branch support for Braid is not yet implemented"
  208. else
  209. in_root do
  210. run("braid add git://github.com/rails/rails.git vendor/rails")
  211. log "rails installed with Braid"
  212. end
  213. end
  214. end
  215. # cloning rails is experimental and somewhat untested
  216. def clone_rails(options={})
  217. if options[:submodule]
  218. in_root do
  219. if options[:branch] && options[:branch] != "master"
  220. git :submodule => "add git://github.com/rails/rails.git vendor/rails -b #{options[:branch]}"
  221. else
  222. git :submodule => "add git://github.com/rails/rails.git vendor/rails"
  223. end
  224. end
  225. else
  226. inside 'vendor' do
  227. run('git clone git://github.com/rails/rails.git')
  228. end
  229. if options[:branch] && options[:branch] != "master"
  230. inside 'vendor/rails' do
  231. run("git branch --track #{options[:branch]} origin/#{options[:branch]}")
  232. run("git checkout #{options[:branch]}")
  233. end
  234. end
  235. end
  236. log "rails installed #{'and submoduled ' if options[:submodule]}from GitHub", options[:branch]
  237. end
  238. # Rails Management
  239. # update rails bits in application after vendoring a new copy of rails
  240. # we need to do this the hard way because we want to overwrite without warning
  241. # TODO: Can we introspect the actual rake:update task to get a current list of subtasks?
  242. def update_app
  243. in_root do
  244. run("echo 'a' | rake rails:update:scripts")
  245. run("echo 'a' | rake rails:update:javascripts")
  246. run("echo 'a' | rake rails:update:configs")
  247. run("echo 'a' | rake rails:update:application_controller")
  248. end
  249. end
  250. # remove the prototype framework
  251. def remove_prototype
  252. run "rm public/javascripts/controls.js"
  253. run "rm public/javascripts/dragdrop.js"
  254. run "rm public/javascripts/effects.js"
  255. run "rm public/javascripts/prototype.js"
  256. end
  257. def install_plugin (name, options)
  258. case @branch_management
  259. when 'none'
  260. plugin name, options
  261. when 'piston'
  262. piston_plugin name, options
  263. when 'braid'
  264. braid_plugin name, options
  265. when 'git'
  266. plugin name, options.merge(:submodule => true)
  267. end
  268. end
  269. def install_rails (options)
  270. case @branch_management
  271. when 'none'
  272. clone_rails options
  273. when 'piston'
  274. piston_rails options
  275. when 'braid'
  276. braid_rails options
  277. when 'git'
  278. clone_rails options.merge(:submodule => true)
  279. end
  280. end
  281. # Mocking generators
  282. def generate_stub(object_name, method_name, return_value)
  283. if @mocking == "rr"
  284. "stub(#{object_name}).#{method_name}{ #{return_value} }"
  285. elsif @mocking == "mocha"
  286. "#{object_name}.stubs(:#{method_name}).returns(#{return_value})"
  287. end
  288. end
  289. def generate_any_instance_stub(object_name, method_name, return_value)
  290. if @mocking == "rr"
  291. "stub.instance_of(#{object_name}).#{method_name}{ #{return_value} }"
  292. elsif @mocking == "mocha"
  293. "#{object_name}.any_instance.stubs(:#{method_name}).returns(#{return_value})"
  294. end
  295. end
  296. def generate_expectation(object_name, method_name, parameter = nil)
  297. if parameter
  298. if @mocking == "rr"
  299. "mock(#{object_name}).#{method_name}(#{parameter})"
  300. elsif @mocking == "mocha"
  301. "#{object_name}.expects(:#{method_name}).with(#{parameter})"
  302. end
  303. else
  304. if @mocking == "rr"
  305. "mock(#{object_name}).#{method_name}"
  306. elsif @mocking == "mocha"
  307. "#{object_name}.expects(:#{method_name})"
  308. end
  309. end
  310. end
  311. def generate_pure_stub(stub_name)
  312. if @mocking == "rr"
  313. "stub!('#{stub_name}')"
  314. elsif @mocking == "mocha"
  315. "stub('#{stub_name}')"
  316. end
  317. end
  318. # Heroku management
  319. # Run a command with the Heroku gem.
  320. #
  321. # ==== Examples
  322. #
  323. # heroku :create
  324. # heroku :rake => "db:migrate"
  325. #
  326. def heroku(command = {})
  327. in_root do
  328. if command.is_a?(Symbol)
  329. log 'running', "heroku #{command}"
  330. run "heroku #{command}"
  331. else
  332. command.each do |command, options|
  333. log 'running', "heroku #{command} #{options}"
  334. run("heroku #{command} #{options}")
  335. end
  336. end
  337. end
  338. end
  339. end
  340. end