PageRenderTime 58ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/railties/lib/rails/generators/app_base.rb

https://gitlab.com/mpivaa/rails
Ruby | 362 lines | 308 code | 41 blank | 13 comment | 19 complexity | 15ed7631640a514d520836a104d11c9c MD5 | raw file
  1. require 'digest/md5'
  2. require 'active_support/core_ext/string/strip'
  3. require 'rails/version' unless defined?(Rails::VERSION)
  4. require 'open-uri'
  5. require 'uri'
  6. require 'rails/generators'
  7. require 'active_support/core_ext/array/extract_options'
  8. module Rails
  9. module Generators
  10. class AppBase < Base # :nodoc:
  11. DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver )
  12. JDBC_DATABASES = %w( jdbcmysql jdbcsqlite3 jdbcpostgresql jdbc )
  13. DATABASES.concat(JDBC_DATABASES)
  14. attr_accessor :rails_template
  15. add_shebang_option!
  16. argument :app_path, type: :string
  17. def self.strict_args_position
  18. false
  19. end
  20. def self.add_shared_options_for(name)
  21. class_option :template, type: :string, aliases: '-m',
  22. desc: "Path to some #{name} template (can be a filesystem path or URL)"
  23. class_option :skip_gemfile, type: :boolean, default: false,
  24. desc: "Don't create a Gemfile"
  25. class_option :skip_bundle, type: :boolean, aliases: '-B', default: false,
  26. desc: "Don't run bundle install"
  27. class_option :skip_git, type: :boolean, aliases: '-G', default: false,
  28. desc: 'Skip .gitignore file'
  29. class_option :skip_keeps, type: :boolean, default: false,
  30. desc: 'Skip source control .keep files'
  31. class_option :skip_action_mailer, type: :boolean, aliases: "-M",
  32. default: false,
  33. desc: "Skip Action Mailer files"
  34. class_option :skip_active_record, type: :boolean, aliases: '-O', default: false,
  35. desc: 'Skip Active Record files'
  36. class_option :skip_sprockets, type: :boolean, aliases: '-S', default: false,
  37. desc: 'Skip Sprockets files'
  38. class_option :skip_spring, type: :boolean, default: false,
  39. desc: "Don't install Spring application preloader"
  40. class_option :database, type: :string, aliases: '-d', default: 'sqlite3',
  41. desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
  42. class_option :javascript, type: :string, aliases: '-j', default: 'jquery',
  43. desc: 'Preconfigure for selected JavaScript library'
  44. class_option :skip_javascript, type: :boolean, aliases: '-J', default: false,
  45. desc: 'Skip JavaScript files'
  46. class_option :dev, type: :boolean, default: false,
  47. desc: "Setup the #{name} with Gemfile pointing to your Rails checkout"
  48. class_option :edge, type: :boolean, default: false,
  49. desc: "Setup the #{name} with Gemfile pointing to Rails repository"
  50. class_option :skip_turbolinks, type: :boolean, default: false,
  51. desc: 'Skip turbolinks gem'
  52. class_option :skip_test, type: :boolean, aliases: '-T', default: false,
  53. desc: 'Skip test files'
  54. class_option :rc, type: :string, default: false,
  55. desc: "Path to file containing extra configuration options for rails command"
  56. class_option :no_rc, type: :boolean, default: false,
  57. desc: 'Skip loading of extra configuration options from .railsrc file'
  58. class_option :help, type: :boolean, aliases: '-h', group: :rails,
  59. desc: 'Show this help message and quit'
  60. end
  61. def initialize(*args)
  62. @gem_filter = lambda { |gem| true }
  63. @extra_entries = []
  64. super
  65. convert_database_option_for_jruby
  66. end
  67. protected
  68. def gemfile_entry(name, *args)
  69. options = args.extract_options!
  70. version = args.first
  71. github = options[:github]
  72. path = options[:path]
  73. if github
  74. @extra_entries << GemfileEntry.github(name, github)
  75. elsif path
  76. @extra_entries << GemfileEntry.path(name, path)
  77. else
  78. @extra_entries << GemfileEntry.version(name, version)
  79. end
  80. self
  81. end
  82. def gemfile_entries
  83. [rails_gemfile_entry,
  84. database_gemfile_entry,
  85. assets_gemfile_entry,
  86. javascript_gemfile_entry,
  87. jbuilder_gemfile_entry,
  88. psych_gemfile_entry,
  89. @extra_entries].flatten.find_all(&@gem_filter)
  90. end
  91. def add_gem_entry_filter
  92. @gem_filter = lambda { |next_filter, entry|
  93. yield(entry) && next_filter.call(entry)
  94. }.curry[@gem_filter]
  95. end
  96. def builder
  97. @builder ||= begin
  98. builder_class = get_builder_class
  99. builder_class.include(ActionMethods)
  100. builder_class.new(self)
  101. end
  102. end
  103. def build(meth, *args)
  104. builder.send(meth, *args) if builder.respond_to?(meth)
  105. end
  106. def create_root
  107. valid_const?
  108. empty_directory '.'
  109. FileUtils.cd(destination_root) unless options[:pretend]
  110. end
  111. def apply_rails_template
  112. apply rails_template if rails_template
  113. rescue Thor::Error, LoadError, Errno::ENOENT => e
  114. raise Error, "The template [#{rails_template}] could not be loaded. Error: #{e}"
  115. end
  116. def set_default_accessors!
  117. self.destination_root = File.expand_path(app_path, destination_root)
  118. self.rails_template = case options[:template]
  119. when /^https?:\/\//
  120. options[:template]
  121. when String
  122. File.expand_path(options[:template], Dir.pwd)
  123. else
  124. options[:template]
  125. end
  126. end
  127. def database_gemfile_entry
  128. return [] if options[:skip_active_record]
  129. GemfileEntry.version gem_for_database, nil,
  130. "Use #{options[:database]} as the database for Active Record"
  131. end
  132. def include_all_railties?
  133. options.values_at(:skip_active_record, :skip_action_mailer, :skip_test, :skip_sprockets).none?
  134. end
  135. def comment_if(value)
  136. options[value] ? '# ' : ''
  137. end
  138. def sqlite3?
  139. !options[:skip_active_record] && options[:database] == 'sqlite3'
  140. end
  141. class GemfileEntry < Struct.new(:name, :version, :comment, :options, :commented_out)
  142. def initialize(name, version, comment, options = {}, commented_out = false)
  143. super
  144. end
  145. def self.github(name, github, branch = nil, comment = nil)
  146. if branch
  147. new(name, nil, comment, github: github, branch: branch)
  148. else
  149. new(name, nil, comment, github: github)
  150. end
  151. end
  152. def self.version(name, version, comment = nil)
  153. new(name, version, comment)
  154. end
  155. def self.path(name, path, comment = nil)
  156. new(name, nil, comment, path: path)
  157. end
  158. end
  159. def rails_gemfile_entry
  160. if options.dev?
  161. [
  162. GemfileEntry.path('rails', Rails::Generators::RAILS_DEV_PATH),
  163. GemfileEntry.github('arel', 'rails/arel')
  164. ]
  165. elsif options.edge?
  166. [
  167. GemfileEntry.github('rails', 'rails/rails'),
  168. GemfileEntry.github('arel', 'rails/arel')
  169. ]
  170. else
  171. [GemfileEntry.version('rails',
  172. Rails::VERSION::STRING,
  173. "Bundle edge Rails instead: gem 'rails', github: 'rails/rails'")]
  174. end
  175. end
  176. def gem_for_database
  177. # %w( mysql oracle postgresql sqlite3 frontbase ibm_db sqlserver jdbcmysql jdbcsqlite3 jdbcpostgresql )
  178. case options[:database]
  179. when "oracle" then "ruby-oci8"
  180. when "postgresql" then "pg"
  181. when "frontbase" then "ruby-frontbase"
  182. when "mysql" then "mysql2"
  183. when "sqlserver" then "activerecord-sqlserver-adapter"
  184. when "jdbcmysql" then "activerecord-jdbcmysql-adapter"
  185. when "jdbcsqlite3" then "activerecord-jdbcsqlite3-adapter"
  186. when "jdbcpostgresql" then "activerecord-jdbcpostgresql-adapter"
  187. when "jdbc" then "activerecord-jdbc-adapter"
  188. else options[:database]
  189. end
  190. end
  191. def convert_database_option_for_jruby
  192. if defined?(JRUBY_VERSION)
  193. case options[:database]
  194. when "oracle" then options[:database].replace "jdbc"
  195. when "postgresql" then options[:database].replace "jdbcpostgresql"
  196. when "mysql" then options[:database].replace "jdbcmysql"
  197. when "sqlite3" then options[:database].replace "jdbcsqlite3"
  198. end
  199. end
  200. end
  201. def assets_gemfile_entry
  202. return [] if options[:skip_sprockets]
  203. gems = []
  204. gems << GemfileEntry.version('sass-rails', '~> 5.0',
  205. 'Use SCSS for stylesheets')
  206. gems << GemfileEntry.version('uglifier',
  207. '>= 1.3.0',
  208. 'Use Uglifier as compressor for JavaScript assets')
  209. gems
  210. end
  211. def jbuilder_gemfile_entry
  212. comment = 'Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder'
  213. GemfileEntry.version('jbuilder', '~> 2.0', comment)
  214. end
  215. def coffee_gemfile_entry
  216. comment = 'Use CoffeeScript for .coffee assets and views'
  217. if options.dev? || options.edge?
  218. GemfileEntry.github 'coffee-rails', 'rails/coffee-rails', nil, comment
  219. else
  220. GemfileEntry.version 'coffee-rails', '~> 4.1.0', comment
  221. end
  222. end
  223. def javascript_gemfile_entry
  224. if options[:skip_javascript]
  225. []
  226. else
  227. gems = [coffee_gemfile_entry, javascript_runtime_gemfile_entry]
  228. gems << GemfileEntry.version("#{options[:javascript]}-rails", nil,
  229. "Use #{options[:javascript]} as the JavaScript library")
  230. unless options[:skip_turbolinks]
  231. gems << GemfileEntry.version("turbolinks", nil,
  232. "Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks")
  233. end
  234. gems
  235. end
  236. end
  237. def javascript_runtime_gemfile_entry
  238. comment = 'See https://github.com/sstephenson/execjs#readme for more supported runtimes'
  239. if defined?(JRUBY_VERSION)
  240. GemfileEntry.version 'therubyrhino', nil, comment
  241. else
  242. GemfileEntry.new 'therubyracer', nil, comment, { platforms: :ruby }, true
  243. end
  244. end
  245. def psych_gemfile_entry
  246. return [] unless defined?(Rubinius)
  247. comment = 'Use Psych as the YAML engine, instead of Syck, so serialized ' \
  248. 'data can be read safely from different rubies (see http://git.io/uuLVag)'
  249. GemfileEntry.new('psych', '~> 2.0', comment, platforms: :rbx)
  250. end
  251. def bundle_command(command)
  252. say_status :run, "bundle #{command}"
  253. # We are going to shell out rather than invoking Bundler::CLI.new(command)
  254. # because `rails new` loads the Thor gem and on the other hand bundler uses
  255. # its own vendored Thor, which could be a different version. Running both
  256. # things in the same process is a recipe for a night with paracetamol.
  257. #
  258. # We use backticks and #print here instead of vanilla #system because it
  259. # is easier to silence stdout in the existing test suite this way. The
  260. # end-user gets the bundler commands called anyway, so no big deal.
  261. #
  262. # We unset temporary bundler variables to load proper bundler and Gemfile.
  263. #
  264. # Thanks to James Tucker for the Gem tricks involved in this call.
  265. _bundle_command = Gem.bin_path('bundler', 'bundle')
  266. require 'bundler'
  267. Bundler.with_clean_env do
  268. output = `"#{Gem.ruby}" "#{_bundle_command}" #{command}`
  269. print output unless options[:quiet]
  270. end
  271. end
  272. def bundle_install?
  273. !(options[:skip_gemfile] || options[:skip_bundle] || options[:pretend])
  274. end
  275. def spring_install?
  276. !options[:skip_spring] && Process.respond_to?(:fork) && !RUBY_PLATFORM.include?("cygwin")
  277. end
  278. def run_bundle
  279. bundle_command('install') if bundle_install?
  280. end
  281. def generate_spring_binstubs
  282. if bundle_install? && spring_install?
  283. bundle_command("exec spring binstub --all")
  284. end
  285. end
  286. def empty_directory_with_keep_file(destination, config = {})
  287. empty_directory(destination, config)
  288. keep_file(destination)
  289. end
  290. def keep_file(destination)
  291. create_file("#{destination}/.keep") unless options[:skip_keeps]
  292. end
  293. end
  294. end
  295. end