roger 0.0.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +2 -0
  3. data/.travis.yml +12 -0
  4. data/CHANGELOG.md +102 -0
  5. data/Gemfile +5 -0
  6. data/MIT_LICENSE +20 -0
  7. data/README.md +10 -10
  8. data/Rakefile +9 -0
  9. data/bin/roger +5 -0
  10. data/doc/cli.md +46 -0
  11. data/doc/mockupfile.md +3 -0
  12. data/doc/templating.md +88 -0
  13. data/examples/default_template/.gitignore +2 -0
  14. data/examples/default_template/CHANGELOG +0 -0
  15. data/examples/default_template/Gemfile +3 -0
  16. data/examples/default_template/Mockupfile +1 -0
  17. data/examples/default_template/html/.empty_directory +0 -0
  18. data/examples/default_template/partials/.empty_directory +0 -0
  19. data/lib/roger/cli/command.rb +23 -0
  20. data/lib/roger/cli/generate.rb +5 -0
  21. data/lib/roger/cli/release.rb +10 -0
  22. data/lib/roger/cli/serve.rb +29 -0
  23. data/lib/roger/cli.rb +123 -0
  24. data/lib/roger/extractor.rb +95 -0
  25. data/lib/roger/generators/generator.rb +23 -0
  26. data/lib/roger/generators/new.rb +67 -0
  27. data/lib/roger/generators/templates/generator.tt +13 -0
  28. data/lib/roger/generators.rb +23 -0
  29. data/lib/roger/mockupfile.rb +63 -0
  30. data/lib/roger/project.rb +92 -0
  31. data/lib/roger/rack/html_validator.rb +26 -0
  32. data/lib/roger/rack/roger.rb +52 -0
  33. data/lib/roger/rack/sleep.rb +21 -0
  34. data/lib/roger/release/cleaner.rb +47 -0
  35. data/lib/roger/release/finalizers/dir.rb +29 -0
  36. data/lib/roger/release/finalizers/git_branch.rb +92 -0
  37. data/lib/roger/release/finalizers/rsync.rb +77 -0
  38. data/lib/roger/release/finalizers/zip.rb +42 -0
  39. data/lib/roger/release/finalizers.rb +19 -0
  40. data/lib/roger/release/injector.rb +99 -0
  41. data/lib/roger/release/processors/mockup.rb +93 -0
  42. data/lib/roger/release/processors/url_relativizer.rb +45 -0
  43. data/lib/roger/release/processors.rb +17 -0
  44. data/lib/roger/release/scm/git.rb +101 -0
  45. data/lib/roger/release/scm.rb +32 -0
  46. data/lib/roger/release.rb +363 -0
  47. data/lib/roger/resolver.rb +119 -0
  48. data/lib/roger/server.rb +117 -0
  49. data/lib/roger/template.rb +206 -0
  50. data/lib/roger/w3c_validator.rb +129 -0
  51. data/roger.gemspec +35 -0
  52. data/test/Mockupfile-syntax.rb +85 -0
  53. data/test/project/.rvmrc +1 -0
  54. data/test/project/Gemfile +7 -0
  55. data/test/project/Gemfile.lock +38 -0
  56. data/test/project/Mockupfile +13 -0
  57. data/test/project/html/formats/erb.html.erb +5 -0
  58. data/test/project/html/formats/index.html +1 -0
  59. data/test/project/html/formats/json.json.erb +0 -0
  60. data/test/project/html/formats/markdown.md +3 -0
  61. data/test/project/html/formats/mockup.html +5 -0
  62. data/test/project/html/front_matter/erb.html.erb +16 -0
  63. data/test/project/html/front_matter/markdown.md +7 -0
  64. data/test/project/html/layouts/content-for.html.erb +17 -0
  65. data/test/project/html/layouts/erb.html.erb +19 -0
  66. data/test/project/html/mockup/encoding.html +3 -0
  67. data/test/project/html/partials/erb.html.erb +10 -0
  68. data/test/project/html/partials/load_path.html.erb +3 -0
  69. data/test/project/html/partials/mockup.html +13 -0
  70. data/test/project/html/static/non-relative.html.erb +9 -0
  71. data/test/project/html/static/relative.html.erb +9 -0
  72. data/test/project/layouts/test.html.erb +34 -0
  73. data/test/project/layouts/yield.html.erb +1 -0
  74. data/test/project/lib/generators/test.rb +9 -0
  75. data/test/project/partials/formats/erb.html.erb +1 -0
  76. data/test/project/partials/partials-test.html.erb +1 -0
  77. data/test/project/partials/test/erb.html.erb +1 -0
  78. data/test/project/partials/test/front_matter.html.erb +1 -0
  79. data/test/project/partials/test/json.json.erb +1 -0
  80. data/test/project/partials/test/markdown.md +1 -0
  81. data/test/project/partials/test/mockup.part.html +1 -0
  82. data/test/project/partials/test/simple.html.erb +1 -0
  83. data/test/project/partials2/partials2-test.html.erb +1 -0
  84. data/test/unit/cli_test.rb +12 -0
  85. data/test/unit/generators_test.rb +75 -0
  86. data/test/unit/release/cleaner_test.rb +47 -0
  87. data/test/unit/resolver_test.rb +92 -0
  88. data/test/unit/template_test.rb +127 -0
  89. metadata +202 -8
@@ -0,0 +1,363 @@
1
+ require File.dirname(__FILE__) + "/cli"
2
+
3
+ module Roger
4
+ class Release
5
+
6
+ attr_reader :config, :project
7
+
8
+ attr_reader :finalizers, :injections, :stack, :cleanups
9
+
10
+ class << self
11
+
12
+ def default_stack
13
+ []
14
+ end
15
+
16
+ def default_finalizers
17
+ [[self.get_callable(:dir, Roger::Release::Finalizers), {}]]
18
+ end
19
+
20
+ # Makes callable into a object that responds to call.
21
+ #
22
+ # @param [#call, Symbol, Class] callable If callable already responds to #call will just return callable, a Symbol will be searched for in the scope parameter, a class will be instantiated (and checked if it will respond to #call)
23
+ # @param [Module] scope The scope in which to search callable in if it's a Symbol
24
+ def get_callable(callable, scope)
25
+ return callable if callable.respond_to?(:call)
26
+
27
+ if callable.kind_of?(Symbol)
28
+ callable = camel_case(callable.to_s).to_sym
29
+ if scope.constants.include?(callable)
30
+ c = scope.const_get(callable)
31
+ callable = c if c.is_a?(Class)
32
+ end
33
+ end
34
+
35
+ if callable.kind_of?(Class)
36
+ callable = callable.new
37
+ end
38
+
39
+ if callable.respond_to?(:call)
40
+ callable
41
+ else
42
+ raise ArgumentError, "Could not resolve #{callable.inspect}. Callable must be an object that responds to #call or a symbol that resolve to such an object or a class with a #call instance method."
43
+ end
44
+
45
+ end
46
+
47
+ # Nothing genius adjusted from:
48
+ # http://stackoverflow.com/questions/9524457/converting-string-from-snake-case-to-camel-case-in-ruby
49
+ def camel_case(string)
50
+ return string if string !~ /_/ && string =~ /[A-Z]+.*/
51
+ string.split('_').map{|e| e.capitalize}.join
52
+ end
53
+
54
+ end
55
+
56
+ # @option config [Symbol] :scm The SCM to use (default = :git)
57
+ # @option config [String, Pathname] :target_path The path/directory to put the release into
58
+ # @option config [String, Pathname]:build_path Temporary path used to build the release
59
+ # @option config [Boolean] :cleanup_build Wether or not to remove the build_path after we're done (default = true)
60
+ def initialize(project, config = {})
61
+ defaults = {
62
+ :scm => :git,
63
+ :source_path => Pathname.new(Dir.pwd) + "html",
64
+ :target_path => Pathname.new(Dir.pwd) + "releases",
65
+ :build_path => Pathname.new(Dir.pwd) + "build",
66
+ :cleanup_build => true
67
+ }
68
+
69
+ @config = {}.update(defaults).update(config)
70
+ @project = project
71
+ @stack = []
72
+ @finalizers = []
73
+ end
74
+
75
+ # Accessor for target_path
76
+ # The target_path is the path where the finalizers will put the release
77
+ #
78
+ # @return Pathname the target_path
79
+ def target_path
80
+ Pathname.new(self.config[:target_path])
81
+ end
82
+
83
+ # Accessor for build_path
84
+ # The build_path is a temporary directory where the release will be built
85
+ #
86
+ # @return Pathname the build_path
87
+ def build_path
88
+ Pathname.new(self.config[:build_path])
89
+ end
90
+
91
+ # Accessor for source_path
92
+ # The source path is the root of the mockup
93
+ #
94
+ # @return Pathanem the source_path
95
+ def source_path
96
+ Pathname.new(self.config[:source_path])
97
+ end
98
+
99
+ # Get the current SCM object
100
+ def scm(force = false)
101
+ return @_scm if @_scm && !force
102
+
103
+ case self.config[:scm]
104
+ when :git
105
+ @_scm = Release::Scm::Git.new(:path => self.source_path)
106
+ else
107
+ raise "Unknown SCM #{options[:scm].inspect}"
108
+ end
109
+ end
110
+
111
+ # Inject variables into files with an optional filter
112
+ #
113
+ # @examples
114
+ # release.inject({"VERSION" => release.version, "DATE" => release.date}, :into => %w{_doc/toc.html})
115
+ # release.inject({"CHANGELOG" => {:file => "", :filter => BlueCloth}}, :into => %w{_doc/changelog.html})
116
+ def inject(variables, options)
117
+ @stack << Injector.new(variables, options)
118
+ end
119
+
120
+ # Use a certain pre-processor
121
+ #
122
+ # @examples
123
+ # release.use :sprockets, sprockets_config
124
+ def use(processor, options = {})
125
+ @stack << [self.class.get_callable(processor, Roger::Release::Processors), options]
126
+ end
127
+
128
+ # Write out the whole release into a directory, zip file or anything you can imagine
129
+ # #finalize can be called multiple times, it just will run all of them.
130
+ #
131
+ # The default finalizer is :dir
132
+ #
133
+ # @param [Symbol, Proc] Finalizer to use
134
+ #
135
+ # @examples
136
+ # release.finalize :zip
137
+ def finalize(finalizer, options = {})
138
+ @finalizers << [self.class.get_callable(finalizer, Roger::Release::Finalizers), options]
139
+ end
140
+
141
+ # Files to clean up in the build directory just before finalization happens
142
+ #
143
+ # @param [String] Pattern to glob within build directory
144
+ #
145
+ # @examples
146
+ # release.cleanup "**/.DS_Store"
147
+ def cleanup(pattern)
148
+ @stack << Cleaner.new(pattern)
149
+ end
150
+
151
+ # Generates a banner if a block is given, or returns the currently set banner.
152
+ # It automatically takes care of adding comment marks around the banner.
153
+ #
154
+ # The default banner looks like this:
155
+ #
156
+ # =======================
157
+ # = Version : v1.0.0 =
158
+ # = Date : 2012-06-20 =
159
+ # =======================
160
+ #
161
+ #
162
+ # @option options [:css,:js,:html,false] :comment Wether or not to comment the output and in what style. (default=js)
163
+ def banner(options = {}, &block)
164
+ options = {
165
+ :comment => :js
166
+ }.update(options)
167
+
168
+ if block_given?
169
+ @_banner = yield.to_s
170
+ elsif !@_banner
171
+ banner = []
172
+ banner << "Version : #{self.scm.version}"
173
+ banner << "Date : #{self.scm.date.strftime("%Y-%m-%d")}"
174
+
175
+ size = banner.inject(0){|mem,b| b.size > mem ? b.size : mem }
176
+ banner.map!{|b| "= #{b.ljust(size)} =" }
177
+ div = "=" * banner.first.size
178
+ banner.unshift(div)
179
+ banner << div
180
+ @_banner = banner.join("\n")
181
+ end
182
+
183
+ if options[:comment]
184
+ self.comment(@_banner, :style => options[:comment])
185
+ else
186
+ @_banner
187
+ end
188
+ end
189
+
190
+ # Extract the mockup, this will happen anyway, and will always happen first
191
+ # This method gives you a way to pass options to the extractor.
192
+ #
193
+ # @param Hash options Options hash passed to extractor
194
+ #
195
+ # @deprecated Don't use the extractor anymore, use release.use(:mockup, options) processor
196
+ def extract(options = {})
197
+ self.warn(self, "Don't use the extractor anymore, use release.use(:mockup, options) and release.use(:url_relativizer, options) processors")
198
+ @extractor_options = options
199
+ end
200
+
201
+ # Actually perform the release
202
+ def run!
203
+ # Validate paths
204
+ validate_paths!
205
+
206
+ # Extract mockup
207
+ copy_source_path_to_build_path!
208
+
209
+ validate_stack!
210
+
211
+ # Run stack
212
+ run_stack!
213
+
214
+ # Run finalizers
215
+ run_finalizers!
216
+
217
+ # Cleanup
218
+ cleanup! if self.config[:cleanup_build]
219
+
220
+ end
221
+
222
+ # Write out a log message
223
+ def log(part, msg, verbose = false, &block)
224
+ if !verbose || verbose && self.project.options[:verbose]
225
+ self.project.shell.say "\033[37m#{part.class.to_s}\033[0m" + " : " + msg.to_s, nil, true
226
+ end
227
+ if block_given?
228
+ begin
229
+ self.project.shell.padding = self.project.shell.padding + 1
230
+ yield
231
+ ensure
232
+ self.project.shell.padding = self.project.shell.padding - 1
233
+ end
234
+ end
235
+ end
236
+
237
+ def debug(part, msg, &block)
238
+ self.log(part, msg, true, &block)
239
+ end
240
+
241
+ # Write out a warning message
242
+ def warn(part, msg)
243
+ self.project.shell.say "\033[37m#{part.class.to_s}\033[0m" + " : " + "\033[31m#{msg.to_s}\033[0m", nil, true
244
+ end
245
+
246
+
247
+ # @param [Array] globs an array of file path globs that will be globbed against the build_path
248
+ # @param [Array] excludes an array of regexps that will be excluded from the result
249
+ def get_files(globs, excludes = [])
250
+ files = globs.map{|g| Dir.glob(self.build_path + g) }.flatten
251
+ if excludes.any?
252
+ files.reject{|c| excludes.detect{|e| e.match(c) } }
253
+ else
254
+ files
255
+ end
256
+ end
257
+
258
+ protected
259
+
260
+ # ==============
261
+ # = The runway =
262
+ # ==============
263
+
264
+ # Checks if build path exists (and cleans it up)
265
+ # Checks if target path exists (if not, creates it)
266
+ def validate_paths!
267
+ if self.build_path.exist?
268
+ log self, "Cleaning up previous build \"#{self.build_path}\""
269
+ rm_rf(self.build_path)
270
+ end
271
+
272
+ if !self.target_path.exist?
273
+ log self, "Creating target path \"#{self.target_path}\""
274
+ mkdir self.target_path
275
+ end
276
+ end
277
+
278
+ # Checks if deprecated extractor options have been set
279
+ # Checks if the mockup will be runned
280
+ def validate_stack!
281
+
282
+ mockup_options = {}
283
+ relativizer_options = {}
284
+ run_relativizer = true
285
+ if @extractor_options
286
+ mockup_options = {:env => @extractor_options[:env]}
287
+ relativizer_options = {:url_attributes => @extractor_options[:url_attributes]}
288
+ run_relativizer = @extractor_options[:url_relativize]
289
+ end
290
+
291
+ unless @stack.find{|(processor, options)| processor.class == Roger::Release::Processors::Mockup }
292
+ @stack.unshift([Roger::Release::Processors::UrlRelativizer.new, relativizer_options])
293
+ @stack.unshift([Roger::Release::Processors::Mockup.new, mockup_options])
294
+ end
295
+ end
296
+
297
+ def copy_source_path_to_build_path!
298
+ mkdir(self.build_path)
299
+ cp_r(self.source_path.children, self.build_path)
300
+ end
301
+
302
+ def run_stack!
303
+ @stack = self.class.default_stack.dup if @stack.empty?
304
+
305
+ # call all objects in @stack
306
+ @stack.each do |task|
307
+ if (task.kind_of?(Array))
308
+ task[0].call(self, task[1])
309
+ else
310
+ task.call(self)
311
+ end
312
+ end
313
+ end
314
+
315
+ def run_finalizers!
316
+ @finalizers = self.class.default_finalizers.dup if @finalizers.empty?
317
+
318
+ # call all objects in @finalizes
319
+ @finalizers.each do |finalizer|
320
+ finalizer[0].call(self, finalizer[1])
321
+ end
322
+
323
+ end
324
+
325
+ def cleanup!
326
+ log(self, "Cleaning up build path #{self.build_path}")
327
+ rm_rf(self.build_path)
328
+ end
329
+
330
+ # @param [String] string The string to comment
331
+ #
332
+ # @option options [:html, :css, :js] :style The comment style to use (default=:js, which is the same as :css)
333
+ # @option options [Boolean] :per_line Comment per line or make one block? (default=true)
334
+ def comment(string, options = {})
335
+ options = {
336
+ :style => :css,
337
+ :per_line => true
338
+ }.update(options)
339
+
340
+ commenters = {
341
+ :html => Proc.new{|s| "<!-- #{s} -->" },
342
+ :css => Proc.new{|s| "/*! #{s} */" },
343
+ :js => Proc.new{|s| "/*! #{s} */" }
344
+ }
345
+
346
+ commenter = commenters[options[:style]] || commenters[:js]
347
+
348
+ if options[:per_line]
349
+ string = string.split(/\r?\n/)
350
+ string.map{|s| commenter.call(s) }.join("\n")
351
+ else
352
+ commenter.call(s)
353
+ end
354
+ end
355
+ end
356
+ end
357
+
358
+ require File.dirname(__FILE__) + "/extractor"
359
+ require File.dirname(__FILE__) + "/release/scm"
360
+ require File.dirname(__FILE__) + "/release/injector"
361
+ require File.dirname(__FILE__) + "/release/cleaner"
362
+ require File.dirname(__FILE__) + "/release/finalizers"
363
+ require File.dirname(__FILE__) + "/release/processors"
@@ -0,0 +1,119 @@
1
+ module Roger
2
+ class Resolver
3
+
4
+ attr_reader :load_paths
5
+
6
+ def initialize(paths)
7
+ raise ArgumentError, "Resolver base path can't be nil" if paths.nil?
8
+
9
+ # Convert to paths
10
+ @load_paths = [paths].flatten.map{|p| Pathname.new(p) }
11
+ end
12
+
13
+ # @param [String] url The url to resolve to a path
14
+ # @param [Hash] options Options
15
+ #
16
+ # @option options [true,false] :exact_match Wether or not to match exact paths, this is mainly used in the path_to_url method to match .js, .css, etc files.
17
+ # @option options [String] :preferred_extension The part to chop off and re-add to search for more complex double-extensions. (Makes it possible to have context aware partials)
18
+ def find_template(url, options = {})
19
+ orig_path, qs, anch = strip_query_string_and_anchor(url.to_s)
20
+
21
+ options = {
22
+ :exact_match => false,
23
+ :preferred_extension => "html"
24
+ }.update(options)
25
+
26
+ paths = self.load_paths.map{|base| File.join(base, orig_path) }
27
+
28
+ paths.find do |path|
29
+ if options[:exact_match] && File.exist?(path)
30
+ return Pathname.new(path)
31
+ end
32
+
33
+ # It's a directory, add "/index"
34
+ if File.directory?(path)
35
+ path = File.join(path, "index")
36
+ end
37
+
38
+ # 2. If it's preferred_extension, we strip of the extension
39
+ if path =~ /\.#{options[:preferred_extension]}\Z/
40
+ path.sub!(/\.#{options[:preferred_extension]}\Z/, "")
41
+ end
42
+
43
+ extensions = Tilt.default_mapping.template_map.keys + Tilt.default_mapping.lazy_map.keys
44
+
45
+ # We have to re-add preferred_extension again as we have stripped it in in step 2!
46
+ extensions += extensions.map{|ext| "#{options[:preferred_extension]}.#{ext}"}
47
+
48
+ if found_extension = extensions.find { |ext| File.exist?(path + "." + ext) }
49
+ return Pathname.new(path + "." + found_extension)
50
+ end
51
+
52
+ false #Next iteration
53
+ end
54
+ end
55
+ alias :url_to_path :find_template
56
+
57
+
58
+ # Convert a disk path on file to an url
59
+ def path_to_url(path, relative_to = nil)
60
+
61
+ # Find the parent path we're in
62
+ path = Pathname.new(path).realpath
63
+ base = self.load_paths.find{|lp| path.to_s =~ /\A#{Regexp.escape(lp.realpath.to_s)}/ }
64
+
65
+ path = path.relative_path_from(base).cleanpath
66
+
67
+ if relative_to
68
+ if relative_to.to_s =~ /\A\//
69
+ relative_to = Pathname.new(File.dirname(relative_to.to_s)).relative_path_from(base).cleanpath
70
+ else
71
+ relative_to = Pathname.new(File.dirname(relative_to.to_s))
72
+ end
73
+ path = Pathname.new("/" + path.to_s).relative_path_from(Pathname.new("/" + relative_to.to_s))
74
+ path.to_s
75
+ else
76
+ "/" + path.to_s
77
+ end
78
+
79
+ end
80
+
81
+ def url_to_relative_url(url, relative_to_path)
82
+ # Skip if the url doesn't start with a / (but not with //)
83
+ return false unless url =~ /\A\/[^\/]/
84
+
85
+ path, qs, anch = strip_query_string_and_anchor(url)
86
+
87
+ # Get disk path
88
+ if true_path = self.url_to_path(path, :exact_match => true)
89
+ path = self.path_to_url(true_path, relative_to_path)
90
+ path += qs if qs
91
+ path += anch if anch
92
+ path
93
+ else
94
+ false
95
+ end
96
+ end
97
+
98
+ def strip_query_string_and_anchor(url)
99
+ url = url.dup
100
+
101
+ # Strip off anchors
102
+ anchor = nil
103
+ url.gsub!(/(#.+)\Z/) do |r|
104
+ anchor = r
105
+ ""
106
+ end
107
+
108
+ # Strip off query strings
109
+ query = nil
110
+ url.gsub!(/(\?.+)\Z/) do |r|
111
+ query = r
112
+ ""
113
+ end
114
+
115
+ [url, query, anchor]
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,117 @@
1
+ require 'rack'
2
+ require File.dirname(__FILE__) + "/template"
3
+ require File.dirname(__FILE__) + "/w3c_validator"
4
+ require File.dirname(__FILE__) + "/rack/roger"
5
+ require File.dirname(__FILE__) + "/rack/html_validator"
6
+
7
+ require 'webrick'
8
+ require 'webrick/https'
9
+
10
+ module Roger
11
+ class Server
12
+
13
+ attr_reader :options, :server_options
14
+
15
+ attr_reader :project
16
+
17
+ attr_accessor :port, :handler
18
+
19
+ def initialize(project, options={})
20
+ @stack = initialize_rack_builder
21
+
22
+ @project = project
23
+
24
+ @server_options = {}
25
+
26
+ set_options(options)
27
+ end
28
+
29
+ # Sets the options, this is a separate method as we want to override certain
30
+ # things set in the mockupfile from the commandline
31
+ def set_options(options)
32
+ @options = {
33
+ :handler => nil, # Autodetect
34
+ :port => 9000
35
+ }.update(options)
36
+
37
+ self.port = @options[:port]
38
+ self.handler = @options[:handler]
39
+ end
40
+
41
+ # Use the specified Rack middleware
42
+ #
43
+ # @see ::Rack::Builder#use
44
+ def use(*args, &block)
45
+ @stack.use *args, &block
46
+ end
47
+
48
+ # Use the map handler to map endpoints to certain urls
49
+ #
50
+ # @see ::Rack::Builder#map
51
+ def map(*args, &block)
52
+ @stack.map *args, &block
53
+ end
54
+
55
+ def run!
56
+ self.get_handler(self.handler).run self.application, self.server_options do |server|
57
+ trap(:INT) do
58
+ ## Use thins' hard #stop! if available, otherwise just #stop
59
+ server.respond_to?(:stop!) ? server.stop! : server.stop
60
+ puts "Roger, out!"
61
+ end
62
+ end
63
+ end
64
+ alias :run :run!
65
+
66
+ def port=(p)
67
+ self.server_options[:Port] = p
68
+ end
69
+
70
+ protected
71
+
72
+ # Build the final application that get's run by the Rack Handler
73
+ def application
74
+ return @app if @app
75
+
76
+ @stack.use Rack::HtmlValidator if self.options[:validate]
77
+ @stack.run Rack::Roger.new(self.project)
78
+
79
+ @app = @stack
80
+ end
81
+
82
+ # Initialize the Rack builder instance for this server
83
+ #
84
+ # @return ::Rack::Builder instance
85
+ def initialize_rack_builder
86
+ builder = ::Rack::Builder.new
87
+ builder.use ::Rack::ShowExceptions
88
+ builder.use ::Rack::Lint
89
+ builder.use ::Rack::ConditionalGet
90
+ builder.use ::Rack::Head
91
+
92
+ builder
93
+ end
94
+
95
+ # Get the actual handler for use in the server
96
+ # Will always return a handler, it will try to use the fallbacks
97
+ def get_handler(preferred_handler_name = nil)
98
+ servers = %w[puma mongrel thin webrick]
99
+ servers.unshift(preferred_handler_name) if preferred_handler_name
100
+
101
+ handler = nil
102
+ while((server_name = servers.shift) && handler === nil) do
103
+ begin
104
+ handler = ::Rack::Handler.get(server_name)
105
+ rescue LoadError
106
+ rescue NameError
107
+ end
108
+ end
109
+
110
+ if preferred_handler_name && server_name != preferred_handler_name
111
+ puts "Handler '#{preferred_handler_name}' not found, using fallback ('#{server_name}')."
112
+ end
113
+ handler
114
+ end
115
+
116
+ end
117
+ end