lightrail 0.0.1 → 0.99.0

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.
Files changed (79) hide show
  1. data/.gitignore +7 -0
  2. data/.rspec +4 -0
  3. data/.travis.yml +11 -0
  4. data/CHANGES.md +8 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +19 -0
  7. data/README.md +205 -0
  8. data/Rakefile +5 -0
  9. data/bin/lightrail +2 -1
  10. data/lib/lightrail.rb +2 -0
  11. data/lib/lightrail/action_controller/metal.rb +0 -2
  12. data/lib/lightrail/cli.rb +16 -0
  13. data/lib/lightrail/commands/application.rb +26 -0
  14. data/lib/lightrail/generators.rb +323 -0
  15. data/lib/lightrail/generators/app_base.rb +281 -0
  16. data/lib/lightrail/generators/app_generator.rb +299 -0
  17. data/lib/lightrail/generators/base.rb +378 -0
  18. data/lib/lightrail/generators/templates/Gemfile +25 -0
  19. data/lib/lightrail/generators/templates/README +259 -0
  20. data/lib/lightrail/generators/templates/Rakefile +7 -0
  21. data/lib/lightrail/generators/templates/app/assets/images/rails.png +0 -0
  22. data/lib/lightrail/generators/templates/app/assets/javascripts/application.js.tt +17 -0
  23. data/lib/lightrail/generators/templates/app/assets/stylesheets/application.css +13 -0
  24. data/lib/lightrail/generators/templates/app/controllers/application_controller.rb +3 -0
  25. data/lib/lightrail/generators/templates/app/helpers/application_helper.rb +2 -0
  26. data/lib/lightrail/generators/templates/app/mailers/.empty_directory +0 -0
  27. data/lib/lightrail/generators/templates/app/models/.empty_directory +0 -0
  28. data/lib/lightrail/generators/templates/app/views/layouts/application.html.erb.tt +14 -0
  29. data/lib/lightrail/generators/templates/config.ru +4 -0
  30. data/lib/lightrail/generators/templates/config/application.rb +67 -0
  31. data/lib/lightrail/generators/templates/config/boot.rb +6 -0
  32. data/lib/lightrail/generators/templates/config/databases/frontbase.yml +31 -0
  33. data/lib/lightrail/generators/templates/config/databases/ibm_db.yml +86 -0
  34. data/lib/lightrail/generators/templates/config/databases/jdbc.yml +62 -0
  35. data/lib/lightrail/generators/templates/config/databases/jdbcmysql.yml +33 -0
  36. data/lib/lightrail/generators/templates/config/databases/jdbcpostgresql.yml +43 -0
  37. data/lib/lightrail/generators/templates/config/databases/jdbcsqlite3.yml +20 -0
  38. data/lib/lightrail/generators/templates/config/databases/mysql.yml +51 -0
  39. data/lib/lightrail/generators/templates/config/databases/oracle.yml +39 -0
  40. data/lib/lightrail/generators/templates/config/databases/postgresql.yml +55 -0
  41. data/lib/lightrail/generators/templates/config/databases/sqlite3.yml +25 -0
  42. data/lib/lightrail/generators/templates/config/environment.rb +5 -0
  43. data/lib/lightrail/generators/templates/config/environments/development.rb.tt +38 -0
  44. data/lib/lightrail/generators/templates/config/environments/production.rb.tt +76 -0
  45. data/lib/lightrail/generators/templates/config/environments/test.rb.tt +36 -0
  46. data/lib/lightrail/generators/templates/config/initializers/backtrace_silencers.rb +7 -0
  47. data/lib/lightrail/generators/templates/config/initializers/inflections.rb +15 -0
  48. data/lib/lightrail/generators/templates/config/initializers/mime_types.rb +5 -0
  49. data/lib/lightrail/generators/templates/config/initializers/secret_token.rb.tt +7 -0
  50. data/lib/lightrail/generators/templates/config/initializers/session_store.rb.tt +8 -0
  51. data/lib/lightrail/generators/templates/config/initializers/wrap_parameters.rb.tt +16 -0
  52. data/lib/lightrail/generators/templates/config/locales/en.yml +5 -0
  53. data/lib/lightrail/generators/templates/config/routes.rb +58 -0
  54. data/lib/lightrail/generators/templates/db/seeds.rb.tt +7 -0
  55. data/lib/lightrail/generators/templates/gitignore +16 -0
  56. data/lib/lightrail/generators/templates/public/404.html +26 -0
  57. data/lib/lightrail/generators/templates/public/422.html +26 -0
  58. data/lib/lightrail/generators/templates/public/500.html +25 -0
  59. data/lib/lightrail/generators/templates/public/favicon.ico +0 -0
  60. data/lib/lightrail/generators/templates/public/index.html +241 -0
  61. data/lib/lightrail/generators/templates/public/robots.txt +5 -0
  62. data/lib/lightrail/generators/templates/public/stylesheets/.empty_directory +0 -0
  63. data/lib/lightrail/generators/templates/script/rails +5 -0
  64. data/lib/lightrail/generators/templates/test/fixtures/.empty_directory +0 -0
  65. data/lib/lightrail/generators/templates/test/functional/.empty_directory +0 -0
  66. data/lib/lightrail/generators/templates/test/integration/.empty_directory +0 -0
  67. data/lib/lightrail/generators/templates/test/performance/browsing_test.rb +12 -0
  68. data/lib/lightrail/generators/templates/test/test_helper.rb +15 -0
  69. data/lib/lightrail/generators/templates/test/unit/.empty_directory +0 -0
  70. data/lib/lightrail/version.rb +1 -1
  71. data/lightrail.gemspec +23 -0
  72. data/logo.png +0 -0
  73. data/spec/lightrail/action_controller/metal_spec.rb +8 -0
  74. data/spec/spec_helper.rb +1 -0
  75. data/tasks/rspec.task +7 -0
  76. metadata +105 -13
  77. data/lib/lightrail/action_controller/param.rb +0 -12
  78. data/lib/lightrail/core_ext/regexp.rb +0 -7
  79. data/lib/lightrail/encryptor.rb +0 -62
@@ -0,0 +1,7 @@
1
+ coverage/
2
+ rdoc/
3
+ doc/
4
+ pkg/
5
+ .yardoc
6
+ .bundle
7
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format documentation
3
+ --backtrace
4
+ --default_path spec
@@ -0,0 +1,11 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - ree
6
+ - ruby-head
7
+ - jruby-18mode
8
+ - jruby-19mode
9
+ - jruby-head
10
+ - rbx-18mode
11
+ - rbx-19mode
@@ -0,0 +1,8 @@
1
+ 0.99.0
2
+ ------
3
+ * Not quite 1.0-pre quality yet, but better!
4
+ * Import Rails generators, generate lightrail apps
5
+
6
+ 0.0.1
7
+ -----
8
+ * Stealth release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 José Valim, Carl Lerche, and contributors
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,205 @@
1
+ ![Lightrail](https://github.com/lightness/lightrail/raw/master/logo.png)
2
+ ============
3
+ [![Build Status](https://secure.travis-ci.org/lightness/lightrail.png?branch=master)](http://travis-ci.org/lightness/lightrail)
4
+
5
+ Lightrail is a minimalist Rails 3 stack for apps that serve primarily APIs,
6
+ with a particular focus on JSON APIs. If [Sinatra][sinatra] doesn't give
7
+ you enough, but [Rails][rails] is still too much, Lightrail is for you.
8
+
9
+ [sinatra]: http://www.sinatrarb.com/
10
+ [rails]: http://rubyonrails.org/
11
+
12
+ Join the mailing list by sending a message to: lightrail@librelist.com
13
+
14
+ Getting Started
15
+ ---------------
16
+
17
+ Install the lightrail gem:
18
+
19
+ `gem install lightrail`
20
+
21
+ Like Rails, installing the lightrail gem will install a command line utility
22
+ called 'lightrail'. This command is in fact identical to the 'rails' command,
23
+ but tweaked for Lightrail defaults instead of Rails defaults.
24
+
25
+ You can use 'lightrail' to create a new application skeleton just like Rails:
26
+
27
+ `lightrail new myapp`
28
+
29
+ The skeleton application that Lightrail generates is identical to a standard
30
+ Rails application, with only these changes:
31
+
32
+ * Gemfile pulls in lightrail instead of rails
33
+ * application.rb pulls in lightrail instead of rails
34
+ * ApplicationController descends from Lightrail::ActionController::Metal
35
+ instead of ActionController::Base. ActionView is not used or installed.
36
+
37
+ Once you've created your application, run:
38
+
39
+ `lightrail server`
40
+
41
+ to launch a web server in the development environment (just like Rails!)
42
+
43
+ You can convert an existing Rails 3 application to a Lightrail application
44
+ by retrofitting the changes mentioned above.
45
+
46
+ Lightrail::ActionController::Metal
47
+ ----------------------------------
48
+
49
+ A lightweight `ActionController::Base` replacement designed for when APIs are your main concern.
50
+ It removes several irrelevant modules and also provides following additional behaviors:
51
+
52
+ * `halt` stops rendering at any point using Ruby's throw/catch mechanism.
53
+ Any option passed to `halt` is forwarded to the `render` method
54
+
55
+ * `render :errors` is a renderer extension that allows you to easily render an error as JSON.
56
+ It is simply a convenience method for `render json: errors, status: 422`.
57
+ With the `halt` mechanism above you'll see this common pattern: `halt errors: { request: "invalid" }`.
58
+
59
+
60
+ Lightrail::Wrapper
61
+ ------------------
62
+
63
+ Wrappers are Lightrail's view replacement, and handle JSON serialization of your models.
64
+ Instead of having a monster `#to_json` method in your model, you can factor that into a
65
+ wrapper instead, and wrappers will automatically take care of many additional JSON
66
+ serialization concerns for you.
67
+
68
+ **Creating A Wrapper**
69
+
70
+ Each model needs to have a wrapper in order to be rendered as JSON.
71
+ Instead of using several options (like `:only`, `:method`, and friends) it expects you to explicitly define the hash to returned through the `view` method.
72
+ Here is an example:
73
+
74
+ ``` ruby
75
+ class AccountWrapper < Lightrail::Wrapper::Model
76
+ has_one :credit_card
77
+ has_one :subscription
78
+
79
+ def view
80
+ attributes = [:id, :name, :user_id]
81
+
82
+ if owner?
83
+ attributes.concat [:billing_address, :billing_country]
84
+ end
85
+
86
+ # Shortcut for account.attributes.slice()
87
+ hash = account.slice(*attributes)
88
+ hash[:owner] = owner?
89
+ hash
90
+ end
91
+
92
+ # Whenever an association method is defined explicitly
93
+ # it is given higher preference. That said, whenever
94
+ # including a credit_card, it will invoke this method
95
+ # instead of calling account.credit_card directly.
96
+ def credit_card
97
+ account.credit_card if owner?
98
+ end
99
+
100
+ protected
101
+
102
+ def owner?
103
+ account.owners.include? scope
104
+ end
105
+ end
106
+ ```
107
+
108
+ A wrapper is initialized with two arguments:
109
+ the `resource` which is the `account` in this case and a `scope`.
110
+ In most cases the scope is the `current_user`.
111
+ The idea of having a scope inside the wrapper is to be able to properly handle permissions when exposing a resource.
112
+ In the example above you can notice that a `credit_card` is only exposed if the user actually owns the account being showed.
113
+ Billing information is also hidden except when the user is an `owner?`.
114
+
115
+ Another convenience is that the wrapper can automatically handle associations.
116
+ Associations, when exposed are not nested exposed but rather flat in the JSON here is an example:
117
+
118
+
119
+ ``` json
120
+ {
121
+ "account": {
122
+ "id": 1,
123
+ "name": "Main",
124
+ "user_id": null,
125
+ "credit_card_id": 1
126
+ },
127
+
128
+ "credit_cards": {
129
+ "id": 1,
130
+ "last_4": "3232"
131
+ }
132
+ }
133
+ ```
134
+
135
+ In order to render a wrapper with its associations you can use the `render` method and pass the associations explicitly:
136
+
137
+ ``` ruby
138
+ AccountWrapper.new(@account, current_user).render include: [:credit_card]
139
+ ```
140
+
141
+ Although most of the times this will be done automatically by the controller.
142
+
143
+ **Using The Wrapper In The Controller**
144
+
145
+ `Lightrail::Wrapper::Controller` provides several facilities to use wrappers from the controller:
146
+
147
+ * `#json(resources)` is the main method.
148
+ Given a resource (or an array of resources) it will find the proper wrapper and render it.
149
+ Any include given at `params[:include]` will be validated and passed to the underlying wrapper.
150
+ Consider the following action:
151
+
152
+ ``` ruby
153
+ def last
154
+ json Account.last
155
+ end
156
+ ```
157
+
158
+ When accessed as `/accounts/last` it won't return any credit card or subscription resource in the JSON, unless it is given explicitly as `/accounts/last?include=credit_cards,subscriptions` (in plural).
159
+
160
+ In order for the `json` method to work, a `wrapper_scope` needs to be defined.
161
+ You can usually define it in your `ApplicationController` as follow:
162
+
163
+ ``` ruby
164
+ def wrapper_scope
165
+ current_user
166
+ end
167
+ ```
168
+
169
+ * `errors(resource)` is a method that makes pair with `json(resource)`.
170
+ It basically receives a resource and render its errors.
171
+ For instance, `errors(account)` will return `:errors => { :account => account.errors }`;
172
+
173
+ * `wrap_array(resources)` as the `json` method accepts extra associations to be included through `params[:include]` we need to be careful to not do `N+1` db queries.
174
+ This can be fixed by using the `wrap_array` method that will automatically wrap the given array and preload all associations.
175
+ For instance, you want will to do this in your `index` actions:
176
+
177
+ ``` ruby
178
+ def index
179
+ json wrap_array(current_user.accounts.active.all)
180
+ end
181
+ ```
182
+
183
+ **Active Record Extensions**
184
+
185
+ `Lightrail::Wrapper` provides one Active Record extension method called `#slice()`.
186
+ In order to understand what it does, it is easier to look at the source:
187
+
188
+ ``` ruby
189
+ def slice(*keys)
190
+ keys.map! { |key| key.to_s }
191
+ attributes.slice(*keys)
192
+ end
193
+ ```
194
+
195
+ This method was used in the example showed above.
196
+
197
+
198
+ config.lightrail.*
199
+ ------------------
200
+
201
+ Lightrail adds a `config.lightrail` namespace to your application with two main methods:
202
+
203
+ * `remove_session_middlewares!` removes `ActionDispatch::Cookies`,
204
+ `ActionDispatch::Session::CookieStore` and `ActionDispatch::Flash` middlewares.
205
+ * `remove_browser_middlewares!` removes the `ActionDispatch::BestStandardsSupport` middleware.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ Dir["tasks/**/*.task"].each { |task| load task }
4
+
5
+ task :default => :spec
@@ -1 +1,2 @@
1
- require "rails/cli"
1
+ #!/usr/bin/env ruby
2
+ require "lightrail/cli"
@@ -0,0 +1,2 @@
1
+ require 'lightrail/version'
2
+ require 'lightrail/railtie'
@@ -1,5 +1,4 @@
1
1
  require 'lightrail/action_controller/haltable'
2
- require 'lightrail/action_controller/param'
3
2
 
4
3
  module Lightrail
5
4
  module ActionController
@@ -36,7 +35,6 @@ module Lightrail
36
35
  include ::ActionController::Instrumentation
37
36
 
38
37
  include Haltable
39
- include Param
40
38
  end
41
39
  end
42
40
  end
@@ -0,0 +1,16 @@
1
+ require 'rbconfig'
2
+ require 'rails/script_rails_loader'
3
+
4
+ # If we are inside a Rails application this method performs an exec and thus
5
+ # the rest of this script is not run.
6
+ Rails::ScriptRailsLoader.exec_script_rails!
7
+
8
+ require 'rails/ruby_version_check'
9
+ Signal.trap("INT") { puts; exit(1) }
10
+
11
+ if ARGV.first == 'plugin'
12
+ ARGV.shift
13
+ require 'rails/commands/plugin_new'
14
+ else
15
+ require 'lightrail/commands/application'
16
+ end
@@ -0,0 +1,26 @@
1
+ require 'lightrail/version'
2
+
3
+ if ['--version', '-v'].include?(ARGV.first)
4
+ puts "Lightrail #{Lightrail::VERSION}"
5
+ exit(0)
6
+ end
7
+
8
+ if ARGV.first != "new"
9
+ ARGV[0] = "--help"
10
+ else
11
+ ARGV.shift
12
+ railsrc = File.join(File.expand_path("~"), ".railsrc")
13
+ if File.exist?(railsrc)
14
+ extra_args_string = File.open(railsrc).read
15
+ extra_args = extra_args_string.split(/\n+/).map {|l| l.split}.flatten
16
+ puts "Using #{extra_args.join(" ")} from #{railsrc}"
17
+ ARGV << extra_args
18
+ ARGV.flatten!
19
+ end
20
+ end
21
+
22
+ require 'rubygems' if ARGV.include?("--dev")
23
+ require 'lightrail/generators'
24
+ require 'lightrail/generators/app_generator'
25
+
26
+ Lightrail::Generators::AppGenerator.start
@@ -0,0 +1,323 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'active_support/core_ext/kernel/singleton_class'
4
+ require 'active_support/core_ext/array/extract_options'
5
+ require 'active_support/core_ext/hash/deep_merge'
6
+ require 'active_support/core_ext/module/attribute_accessors'
7
+ require 'active_support/core_ext/string/inflections'
8
+
9
+ require 'lightrail/generators/base'
10
+
11
+ module Lightrail
12
+ module Generators
13
+ autoload :Actions, 'rails/generators/actions'
14
+ autoload :ActiveModel, 'rails/generators/active_model'
15
+ autoload :Migration, 'rails/generators/migration'
16
+ autoload :NamedBase, 'rails/generators/named_base'
17
+ autoload :ResourceHelpers, 'rails/generators/resource_helpers'
18
+ autoload :TestCase, 'rails/generators/test_case'
19
+
20
+ mattr_accessor :namespace
21
+
22
+ DEFAULT_ALIASES = {
23
+ :rails => {
24
+ :actions => '-a',
25
+ :orm => '-o',
26
+ :javascripts => '-j',
27
+ :javascript_engine => '-je',
28
+ :resource_controller => '-c',
29
+ :scaffold_controller => '-c',
30
+ :stylesheets => '-y',
31
+ :stylesheet_engine => '-se',
32
+ :template_engine => '-e',
33
+ :test_framework => '-t'
34
+ },
35
+
36
+ :test_unit => {
37
+ :fixture_replacement => '-r',
38
+ }
39
+ }
40
+
41
+ DEFAULT_OPTIONS = {
42
+ :rails => {
43
+ :assets => true,
44
+ :force_plural => false,
45
+ :helper => true,
46
+ :integration_tool => nil,
47
+ :javascripts => true,
48
+ :javascript_engine => :js,
49
+ :orm => false,
50
+ :performance_tool => nil,
51
+ :resource_controller => :controller,
52
+ :scaffold_controller => :scaffold_controller,
53
+ :stylesheets => true,
54
+ :stylesheet_engine => :css,
55
+ :test_framework => false,
56
+ :template_engine => :erb
57
+ }
58
+ }
59
+
60
+ def self.configure!(config) #:nodoc:
61
+ no_color! unless config.colorize_logging
62
+ aliases.deep_merge! config.aliases
63
+ options.deep_merge! config.options
64
+ fallbacks.merge! config.fallbacks
65
+ templates_path.concat config.templates
66
+ templates_path.uniq!
67
+ hide_namespaces(*config.hidden_namespaces)
68
+ end
69
+
70
+ def self.templates_path
71
+ @templates_path ||= []
72
+ end
73
+
74
+ def self.aliases #:nodoc:
75
+ @aliases ||= DEFAULT_ALIASES.dup
76
+ end
77
+
78
+ def self.options #:nodoc:
79
+ @options ||= DEFAULT_OPTIONS.dup
80
+ end
81
+
82
+ # Hold configured generators fallbacks. If a plugin developer wants a
83
+ # generator group to fallback to another group in case of missing generators,
84
+ # they can add a fallback.
85
+ #
86
+ # For example, shoulda is considered a test_framework and is an extension
87
+ # of test_unit. However, most part of shoulda generators are similar to
88
+ # test_unit ones.
89
+ #
90
+ # Shoulda then can tell generators to search for test_unit generators when
91
+ # some of them are not available by adding a fallback:
92
+ #
93
+ # Rails::Generators.fallbacks[:shoulda] = :test_unit
94
+ #
95
+ def self.fallbacks
96
+ @fallbacks ||= {}
97
+ end
98
+
99
+ # Remove the color from output.
100
+ def self.no_color!
101
+ Thor::Base.shell = Thor::Shell::Basic
102
+ end
103
+
104
+ # Track all generators subclasses.
105
+ def self.subclasses
106
+ @subclasses ||= []
107
+ end
108
+
109
+ # Rails finds namespaces similar to thor, it only adds one rule:
110
+ #
111
+ # Generators names must end with "_generator.rb". This is required because Rails
112
+ # looks in load paths and loads the generator just before it's going to be used.
113
+ #
114
+ # ==== Examples
115
+ #
116
+ # find_by_namespace :webrat, :rails, :integration
117
+ #
118
+ # Will search for the following generators:
119
+ #
120
+ # "rails:webrat", "webrat:integration", "webrat"
121
+ #
122
+ # Notice that "rails:generators:webrat" could be loaded as well, what
123
+ # Rails looks for is the first and last parts of the namespace.
124
+ #
125
+ def self.find_by_namespace(name, base=nil, context=nil) #:nodoc:
126
+ lookups = []
127
+ lookups << "#{base}:#{name}" if base
128
+ lookups << "#{name}:#{context}" if context
129
+
130
+ unless base || context
131
+ unless name.to_s.include?(?:)
132
+ lookups << "#{name}:#{name}"
133
+ lookups << "rails:#{name}"
134
+ end
135
+ lookups << "#{name}"
136
+ end
137
+
138
+ lookup(lookups)
139
+
140
+ namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }]
141
+
142
+ lookups.each do |namespace|
143
+ klass = namespaces[namespace]
144
+ return klass if klass
145
+ end
146
+
147
+ invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
148
+ end
149
+
150
+ # Receives a namespace, arguments and the behavior to invoke the generator.
151
+ # It's used as the default entry point for generate, destroy and update
152
+ # commands.
153
+ def self.invoke(namespace, args=ARGV, config={})
154
+ names = namespace.to_s.split(':')
155
+ if klass = find_by_namespace(names.pop, names.any? && names.join(':'))
156
+ args << "--help" if args.empty? && klass.arguments.any? { |a| a.required? }
157
+ klass.start(args, config)
158
+ else
159
+ puts "Could not find generator #{namespace}."
160
+ end
161
+ end
162
+
163
+ def self.hidden_namespaces
164
+ @hidden_namespaces ||= begin
165
+ orm = options[:rails][:orm]
166
+ test = options[:rails][:test_framework]
167
+ template = options[:rails][:template_engine]
168
+ css = options[:rails][:stylesheet_engine]
169
+
170
+ [
171
+ "rails",
172
+ "#{orm}:migration",
173
+ "#{orm}:model",
174
+ "#{orm}:observer",
175
+ "#{orm}:session_migration",
176
+ "#{test}:controller",
177
+ "#{test}:helper",
178
+ "#{test}:integration",
179
+ "#{test}:mailer",
180
+ "#{test}:model",
181
+ "#{test}:observer",
182
+ "#{test}:scaffold",
183
+ "#{test}:view",
184
+ "#{test}:performance",
185
+ "#{template}:controller",
186
+ "#{template}:scaffold",
187
+ "#{template}:mailer",
188
+ "#{css}:scaffold",
189
+ "#{css}:assets",
190
+ "css:assets",
191
+ "css:scaffold"
192
+ ]
193
+ end
194
+ end
195
+
196
+ class << self
197
+ def hide_namespaces(*namespaces)
198
+ hidden_namespaces.concat(namespaces)
199
+ end
200
+ alias hide_namespace hide_namespaces
201
+ end
202
+
203
+ # Show help message with available generators.
204
+ def self.help(command = 'generate')
205
+ lookup!
206
+
207
+ namespaces = subclasses.map{ |k| k.namespace }
208
+ namespaces.sort!
209
+
210
+ groups = Hash.new { |h,k| h[k] = [] }
211
+ namespaces.each do |namespace|
212
+ base = namespace.split(':').first
213
+ groups[base] << namespace
214
+ end
215
+
216
+ puts "Usage: lightrail #{command} GENERATOR [args] [options]"
217
+ puts
218
+ puts "General options:"
219
+ puts " -h, [--help] # Print generator's options and usage"
220
+ puts " -p, [--pretend] # Run but do not make any changes"
221
+ puts " -f, [--force] # Overwrite files that already exist"
222
+ puts " -s, [--skip] # Skip files that already exist"
223
+ puts " -q, [--quiet] # Suppress status output"
224
+ puts
225
+ puts "Please choose a generator below."
226
+ puts
227
+
228
+ # Print Rails defaults first.
229
+ rails = groups.delete("rails")
230
+ rails.map! { |n| n.sub(/^rails:/, '') }
231
+ rails.delete("app")
232
+ rails.delete("plugin_new")
233
+ print_list("rails", rails)
234
+
235
+ hidden_namespaces.each {|n| groups.delete(n.to_s) }
236
+
237
+ groups.sort.each { |b, n| print_list(b, n) }
238
+ end
239
+
240
+ protected
241
+
242
+ # Prints a list of generators.
243
+ def self.print_list(base, namespaces) #:nodoc:
244
+ namespaces = namespaces.reject do |n|
245
+ hidden_namespaces.include?(n)
246
+ end
247
+
248
+ return if namespaces.empty?
249
+ puts "#{base.camelize}:"
250
+
251
+ namespaces.each do |namespace|
252
+ puts(" #{namespace}")
253
+ end
254
+
255
+ puts
256
+ end
257
+
258
+ # Try fallbacks for the given base.
259
+ def self.invoke_fallbacks_for(name, base) #:nodoc:
260
+ return nil unless base && fallbacks[base.to_sym]
261
+ invoked_fallbacks = []
262
+
263
+ Array(fallbacks[base.to_sym]).each do |fallback|
264
+ next if invoked_fallbacks.include?(fallback)
265
+ invoked_fallbacks << fallback
266
+
267
+ klass = find_by_namespace(name, fallback)
268
+ return klass if klass
269
+ end
270
+
271
+ nil
272
+ end
273
+
274
+ # Receives namespaces in an array and tries to find matching generators
275
+ # in the load path.
276
+ def self.lookup(namespaces) #:nodoc:
277
+ paths = namespaces_to_paths(namespaces)
278
+
279
+ paths.each do |raw_path|
280
+ ["rails/generators", "generators"].each do |base|
281
+ path = "#{base}/#{raw_path}_generator"
282
+
283
+ begin
284
+ require path
285
+ return
286
+ rescue LoadError => e
287
+ raise unless e.message =~ /#{Regexp.escape(path)}$/
288
+ rescue Exception => e
289
+ warn "[WARNING] Could not load generator #{path.inspect}. Error: #{e.message}.\n#{e.backtrace.join("\n")}"
290
+ end
291
+ end
292
+ end
293
+ end
294
+
295
+ # This will try to load any generator in the load path to show in help.
296
+ def self.lookup! #:nodoc:
297
+ $LOAD_PATH.each do |base|
298
+ Dir[File.join(base, "{rails/generators,generators}", "**", "*_generator.rb")].each do |path|
299
+ begin
300
+ path = path.sub("#{base}/", "")
301
+ require path
302
+ rescue Exception
303
+ # No problem
304
+ end
305
+ end
306
+ end
307
+ end
308
+
309
+ # Convert namespaces to paths by replacing ":" for "/" and adding
310
+ # an extra lookup. For example, "rails:model" should be searched
311
+ # in both: "rails/model/model_generator" and "rails/model_generator".
312
+ def self.namespaces_to_paths(namespaces) #:nodoc:
313
+ paths = []
314
+ namespaces.each do |namespace|
315
+ pieces = namespace.split(":")
316
+ paths << pieces.dup.push(pieces.last).join("/")
317
+ paths << pieces.join("/")
318
+ end
319
+ paths.uniq!
320
+ paths
321
+ end
322
+ end
323
+ end