PageRenderTime 59ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/railties/lib/rails/generators/rails/app/app_generator.rb

https://github.com/gavingmiller/rails
Ruby | 512 lines | 437 code | 57 blank | 18 comment | 15 complexity | da8dd81bc90cd2f581965f1e73320813 MD5 | raw file
  1. require "rails/generators/app_base"
  2. module Rails
  3. module ActionMethods # :nodoc:
  4. attr_reader :options
  5. def initialize(generator)
  6. @generator = generator
  7. @options = generator.options
  8. end
  9. private
  10. %w(template copy_file directory empty_directory inside
  11. empty_directory_with_keep_file create_file chmod shebang).each do |method|
  12. class_eval <<-RUBY, __FILE__, __LINE__ + 1
  13. def #{method}(*args, &block)
  14. @generator.send(:#{method}, *args, &block)
  15. end
  16. RUBY
  17. end
  18. # TODO: Remove once this is fully in place
  19. def method_missing(meth, *args, &block)
  20. @generator.send(meth, *args, &block)
  21. end
  22. end
  23. # The application builder allows you to override elements of the application
  24. # generator without being forced to reverse the operations of the default
  25. # generator.
  26. #
  27. # This allows you to override entire operations, like the creation of the
  28. # Gemfile, README, or JavaScript files, without needing to know exactly
  29. # what those operations do so you can create another template action.
  30. class AppBuilder
  31. def rakefile
  32. template "Rakefile"
  33. end
  34. def readme
  35. copy_file "README.md", "README.md"
  36. end
  37. def gemfile
  38. template "Gemfile"
  39. end
  40. def configru
  41. template "config.ru"
  42. end
  43. def gitignore
  44. template "gitignore", ".gitignore"
  45. end
  46. def version_control
  47. if !options[:skip_git] && !options[:pretend]
  48. run "git init"
  49. end
  50. end
  51. def app
  52. directory "app"
  53. keep_file "app/assets/images"
  54. empty_directory_with_keep_file "app/assets/javascripts/channels" unless options[:skip_action_cable]
  55. keep_file "app/controllers/concerns"
  56. keep_file "app/models/concerns"
  57. end
  58. def bin
  59. directory "bin" do |content|
  60. "#{shebang}\n" + content
  61. end
  62. chmod "bin", 0755 & ~File.umask, verbose: false
  63. end
  64. def config
  65. empty_directory "config"
  66. inside "config" do
  67. template "routes.rb"
  68. template "application.rb"
  69. template "environment.rb"
  70. template "secrets.yml"
  71. template "cable.yml" unless options[:skip_action_cable]
  72. template "puma.rb" unless options[:skip_puma]
  73. template "spring.rb" if spring_install?
  74. directory "environments"
  75. directory "initializers"
  76. directory "locales"
  77. end
  78. end
  79. def config_when_updating
  80. cookie_serializer_config_exist = File.exist?("config/initializers/cookies_serializer.rb")
  81. action_cable_config_exist = File.exist?("config/cable.yml")
  82. rack_cors_config_exist = File.exist?("config/initializers/cors.rb")
  83. config
  84. gsub_file "config/environments/development.rb", /^(\s+)config\.file_watcher/, '\1# config.file_watcher'
  85. unless cookie_serializer_config_exist
  86. gsub_file "config/initializers/cookies_serializer.rb", /json(?!,)/, "marshal"
  87. end
  88. unless action_cable_config_exist
  89. template "config/cable.yml"
  90. end
  91. unless rack_cors_config_exist
  92. remove_file "config/initializers/cors.rb"
  93. end
  94. end
  95. def database_yml
  96. template "config/databases/#{options[:database]}.yml", "config/database.yml"
  97. end
  98. def db
  99. directory "db"
  100. end
  101. def lib
  102. empty_directory "lib"
  103. empty_directory_with_keep_file "lib/tasks"
  104. empty_directory_with_keep_file "lib/assets"
  105. end
  106. def log
  107. empty_directory_with_keep_file "log"
  108. end
  109. def public_directory
  110. directory "public", "public", recursive: false
  111. end
  112. def test
  113. empty_directory_with_keep_file "test/fixtures"
  114. empty_directory_with_keep_file "test/fixtures/files"
  115. empty_directory_with_keep_file "test/controllers"
  116. empty_directory_with_keep_file "test/mailers"
  117. empty_directory_with_keep_file "test/models"
  118. empty_directory_with_keep_file "test/helpers"
  119. empty_directory_with_keep_file "test/integration"
  120. template "test/test_helper.rb"
  121. end
  122. def tmp
  123. empty_directory_with_keep_file "tmp"
  124. empty_directory "tmp/cache"
  125. empty_directory "tmp/cache/assets"
  126. end
  127. def vendor
  128. empty_directory_with_keep_file "vendor"
  129. unless options[:skip_yarn]
  130. template "package.json", "vendor/package.json"
  131. end
  132. end
  133. end
  134. module Generators
  135. # We need to store the RAILS_DEV_PATH in a constant, otherwise the path
  136. # can change in Ruby 1.8.7 when we FileUtils.cd.
  137. RAILS_DEV_PATH = File.expand_path("../../../../../..", File.dirname(__FILE__))
  138. RESERVED_NAMES = %w[application destroy plugin runner test]
  139. class AppGenerator < AppBase # :nodoc:
  140. add_shared_options_for "application"
  141. # Add bin/rails options
  142. class_option :version, type: :boolean, aliases: "-v", group: :rails,
  143. desc: "Show Rails version number and quit"
  144. class_option :api, type: :boolean,
  145. desc: "Preconfigure smaller stack for API only apps"
  146. class_option :skip_bundle, type: :boolean, aliases: "-B", default: false,
  147. desc: "Don't run bundle install"
  148. def initialize(*args)
  149. super
  150. if !options[:skip_active_record] && !DATABASES.include?(options[:database])
  151. raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}."
  152. end
  153. # Force sprockets and yarn to be skipped when generating API only apps.
  154. # Can't modify options hash as it's frozen by default.
  155. if options[:api]
  156. self.options = options.merge(skip_sprockets: true, skip_javascript: true, skip_yarn: true).freeze
  157. end
  158. end
  159. public_task :set_default_accessors!
  160. public_task :create_root
  161. def create_root_files
  162. build(:readme)
  163. build(:rakefile)
  164. build(:configru)
  165. build(:gitignore) unless options[:skip_git]
  166. build(:gemfile) unless options[:skip_gemfile]
  167. build(:version_control)
  168. end
  169. def create_app_files
  170. build(:app)
  171. end
  172. def create_bin_files
  173. build(:bin)
  174. end
  175. def create_config_files
  176. build(:config)
  177. end
  178. def update_config_files
  179. build(:config_when_updating)
  180. end
  181. remove_task :update_config_files
  182. def display_upgrade_guide_info
  183. say "\nAfter this, check Rails upgrade guide at http://guides.rubyonrails.org/upgrading_ruby_on_rails.html for more details about upgrading your app."
  184. end
  185. remove_task :display_upgrade_guide_info
  186. def create_boot_file
  187. template "config/boot.rb"
  188. end
  189. def create_active_record_files
  190. return if options[:skip_active_record]
  191. build(:database_yml)
  192. end
  193. def create_db_files
  194. return if options[:skip_active_record]
  195. build(:db)
  196. end
  197. def create_lib_files
  198. build(:lib)
  199. end
  200. def create_log_files
  201. build(:log)
  202. end
  203. def create_public_files
  204. build(:public_directory)
  205. end
  206. def create_test_files
  207. build(:test) unless options[:skip_test]
  208. end
  209. def create_tmp_files
  210. build(:tmp)
  211. end
  212. def create_vendor_files
  213. build(:vendor)
  214. if options[:skip_yarn]
  215. remove_file "vendor/package.json"
  216. end
  217. end
  218. def delete_app_assets_if_api_option
  219. if options[:api]
  220. remove_dir "app/assets"
  221. remove_dir "lib/assets"
  222. remove_dir "tmp/cache/assets"
  223. end
  224. end
  225. def delete_app_helpers_if_api_option
  226. if options[:api]
  227. remove_dir "app/helpers"
  228. remove_dir "test/helpers"
  229. end
  230. end
  231. def delete_application_layout_file_if_api_option
  232. if options[:api]
  233. remove_file "app/views/layouts/application.html.erb"
  234. end
  235. end
  236. def delete_public_files_if_api_option
  237. if options[:api]
  238. remove_file "public/404.html"
  239. remove_file "public/422.html"
  240. remove_file "public/500.html"
  241. remove_file "public/apple-touch-icon-precomposed.png"
  242. remove_file "public/apple-touch-icon.png"
  243. remove_file "public/favicon.ico"
  244. end
  245. end
  246. def delete_js_folder_skipping_javascript
  247. if options[:skip_javascript]
  248. remove_dir "app/assets/javascripts"
  249. end
  250. end
  251. def delete_assets_initializer_skipping_sprockets
  252. if options[:skip_sprockets]
  253. remove_file "config/initializers/assets.rb"
  254. end
  255. end
  256. def delete_application_record_skipping_active_record
  257. if options[:skip_active_record]
  258. remove_file "app/models/application_record.rb"
  259. end
  260. end
  261. def delete_action_mailer_files_skipping_action_mailer
  262. if options[:skip_action_mailer]
  263. remove_file "app/views/layouts/mailer.html.erb"
  264. remove_file "app/views/layouts/mailer.text.erb"
  265. remove_dir "app/mailers"
  266. remove_dir "test/mailers"
  267. end
  268. end
  269. def delete_action_cable_files_skipping_action_cable
  270. if options[:skip_action_cable]
  271. remove_file "config/cable.yml"
  272. remove_file "app/assets/javascripts/cable.js"
  273. remove_dir "app/channels"
  274. end
  275. end
  276. def delete_non_api_initializers_if_api_option
  277. if options[:api]
  278. remove_file "config/initializers/cookies_serializer.rb"
  279. end
  280. end
  281. def delete_api_initializers
  282. unless options[:api]
  283. remove_file "config/initializers/cors.rb"
  284. end
  285. end
  286. def delete_bin_yarn_if_skip_yarn_option
  287. remove_file "bin/yarn" if options[:skip_yarn]
  288. end
  289. def finish_template
  290. build(:leftovers)
  291. end
  292. public_task :apply_rails_template, :run_bundle
  293. public_task :run_webpack, :generate_spring_binstubs
  294. def run_after_bundle_callbacks
  295. @after_bundle_callbacks.each(&:call)
  296. end
  297. def self.banner
  298. "rails new #{arguments.map(&:usage).join(' ')} [options]"
  299. end
  300. private
  301. # Define file as an alias to create_file for backwards compatibility.
  302. def file(*args, &block)
  303. create_file(*args, &block)
  304. end
  305. def app_name
  306. @app_name ||= (defined_app_const_base? ? defined_app_name : File.basename(destination_root)).tr('\\', "").tr(". ", "_")
  307. end
  308. def defined_app_name
  309. defined_app_const_base.underscore
  310. end
  311. def defined_app_const_base
  312. Rails.respond_to?(:application) && defined?(Rails::Application) &&
  313. Rails.application.is_a?(Rails::Application) && Rails.application.class.name.sub(/::Application$/, "")
  314. end
  315. alias :defined_app_const_base? :defined_app_const_base
  316. def app_const_base
  317. @app_const_base ||= defined_app_const_base || app_name.gsub(/\W/, "_").squeeze("_").camelize
  318. end
  319. alias :camelized :app_const_base
  320. def app_const
  321. @app_const ||= "#{app_const_base}::Application"
  322. end
  323. def valid_const?
  324. if app_const =~ /^\d/
  325. raise Error, "Invalid application name #{app_name}. Please give a name which does not start with numbers."
  326. elsif RESERVED_NAMES.include?(app_name)
  327. raise Error, "Invalid application name #{app_name}. Please give a " \
  328. "name which does not match one of the reserved rails " \
  329. "words: #{RESERVED_NAMES.join(", ")}"
  330. elsif Object.const_defined?(app_const_base)
  331. raise Error, "Invalid application name #{app_name}, constant #{app_const_base} is already in use. Please choose another application name."
  332. end
  333. end
  334. def app_secret
  335. SecureRandom.hex(64)
  336. end
  337. def mysql_socket
  338. @mysql_socket ||= [
  339. "/tmp/mysql.sock", # default
  340. "/var/run/mysqld/mysqld.sock", # debian/gentoo
  341. "/var/tmp/mysql.sock", # freebsd
  342. "/var/lib/mysql/mysql.sock", # fedora
  343. "/opt/local/lib/mysql/mysql.sock", # fedora
  344. "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql
  345. "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4
  346. "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5
  347. "/opt/lampp/var/mysql/mysql.sock" # xampp for linux
  348. ].find { |f| File.exist?(f) } unless Gem.win_platform?
  349. end
  350. def get_builder_class
  351. defined?(::AppBuilder) ? ::AppBuilder : Rails::AppBuilder
  352. end
  353. end
  354. # This class handles preparation of the arguments before the AppGenerator is
  355. # called. The class provides version or help information if they were
  356. # requested, and also constructs the railsrc file (used for extra configuration
  357. # options).
  358. #
  359. # This class should be called before the AppGenerator is required and started
  360. # since it configures and mutates ARGV correctly.
  361. class ARGVScrubber # :nodoc:
  362. def initialize(argv = ARGV)
  363. @argv = argv
  364. end
  365. def prepare!
  366. handle_version_request!(@argv.first)
  367. handle_invalid_command!(@argv.first, @argv) do
  368. handle_rails_rc!(@argv.drop(1))
  369. end
  370. end
  371. def self.default_rc_file
  372. File.expand_path("~/.railsrc")
  373. end
  374. private
  375. def handle_version_request!(argument)
  376. if ["--version", "-v"].include?(argument)
  377. require "rails/version"
  378. puts "Rails #{Rails::VERSION::STRING}"
  379. exit(0)
  380. end
  381. end
  382. def handle_invalid_command!(argument, argv)
  383. if argument == "new"
  384. yield
  385. else
  386. ["--help"] + argv.drop(1)
  387. end
  388. end
  389. def handle_rails_rc!(argv)
  390. if argv.find { |arg| arg == "--no-rc" }
  391. argv.reject { |arg| arg == "--no-rc" }
  392. else
  393. railsrc(argv) { |rc_argv, rc| insert_railsrc_into_argv!(rc_argv, rc) }
  394. end
  395. end
  396. def railsrc(argv)
  397. if (customrc = argv.index { |x| x.include?("--rc=") })
  398. fname = File.expand_path(argv[customrc].gsub(/--rc=/, ""))
  399. yield(argv.take(customrc) + argv.drop(customrc + 1), fname)
  400. else
  401. yield argv, self.class.default_rc_file
  402. end
  403. end
  404. def read_rc_file(railsrc)
  405. extra_args = File.readlines(railsrc).flat_map(&:split)
  406. puts "Using #{extra_args.join(" ")} from #{railsrc}"
  407. extra_args
  408. end
  409. def insert_railsrc_into_argv!(argv, railsrc)
  410. return argv unless File.exist?(railsrc)
  411. extra_args = read_rc_file railsrc
  412. argv.take(1) + extra_args + argv.drop(1)
  413. end
  414. end
  415. end
  416. end