Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial commit

  • Loading branch information...
commit 424d010fb168fb5bb8ca8859d020dc3b9defdcd8 0 parents
@rkh authored
Showing with 1,305 additions and 0 deletions.
  1. +10 −0 .gitignore
  2. +27 −0 LICENSE
  3. +154 −0 README.rdoc
  4. +36 −0 README.rdoc.erb
  5. +76 −0 Rakefile
  6. +25 −0 big_band.gemspec
  7. +206 −0 lib/big_band.rb
  8. +102 −0 lib/big_band/advanced_routes.rb
  9. +183 −0 lib/big_band/basic_extensions.rb
  10. +89 −0 lib/big_band/compass.rb
  11. +3 −0  lib/big_band/compass/big_band.rb
  12. +1 −0  lib/big_band/compass/stylesheets/_big_band.sass
  13. 0  lib/big_band/compass/stylesheets/big_band/_blueprint.sass
  14. 0  lib/big_band/compass/stylesheets/big_band/_utilities.sass
  15. +28 −0 lib/big_band/integration.rb
  16. +13 −0 lib/big_band/integration/monk.rb
  17. +29 −0 lib/big_band/integration/rake.rb
  18. +15 −0 lib/big_band/integration/rspec.rb
  19. +74 −0 lib/big_band/integration/yard.rb
  20. +14 −0 lib/big_band/more_server.rb
  21. +13 −0 lib/big_band/more_server/rainbows.rb
  22. +28 −0 lib/big_band/more_server/unicorn.rb
  23. +73 −0 lib/big_band/reloader.rb
  24. +55 −0 lib/big_band/web_inspector.rb
  25. +6 −0 lib/big_bang.rb
  26. +4 −0 spec/big_band/advanced_routes_spec.rb
  27. +20 −0 spec/big_band/basic_extensions_spec.rb
  28. +4 −0 spec/big_band/compass_spec.rb
  29. +4 −0 spec/big_band/more_server_spec.rb
  30. +4 −0 spec/big_band/reloader_spec.rb
  31. +4 −0 spec/spec.opts
  32. +5 −0 spec/spec_helper.rb
10 .gitignore
@@ -0,0 +1,10 @@
+/doc
+/pkg
+.DS_Store
+.yardoc
+.sass-cache
+*.gem
+*.rbx
+*.rbc
+*.log
+*~
27 LICENSE
@@ -0,0 +1,27 @@
+copyright (c) 2009 Konstantin Haase. All rights reserved.
+
+Developed by: Konstantin Haase
+ http://github.com/rkh/big_band
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal with the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimers.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimers in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of Konstantin Haase, nor the names of other contributors
+ may be used to endorse or promote products derived from this Software without
+ specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+WITH THE SOFTWARE.
154 README.rdoc
@@ -0,0 +1,154 @@
+= BigBand
+BigBand is a collection of Sinatra extensions.
+
+== Usage
+
+Using all BigBand features:
+
+ require "big_band"
+ class Example < BigBand
+ # Yay, BigBand!
+ end
+
+Or for the lazy folks (read: you would don't subclass Sinatra::Base on your own):
+
+ require "sinatra"
+ require "big_band"
+ # Yay, BigBand!
+
+Using just your favorite BigBand features:
+
+ require "big_band"
+ class Example < Sinatra::Base
+ register BigBand::SomeFeature
+ # Yay, BigBand::SomeFeature!
+ end
+
+Or, if you like a more handy syntax:
+
+ require "big_band"
+ class Example < BigBand :SomeFeature, MyStuff::Extension, :development => :DevelopmentOnlyFeature
+ # Yay, BigBand::SomeFeature!
+ # Yay, MyStuff::Extension!
+ # Yay, BigBand::DevelopmentOnlyFeature, if this is development mode!
+ end
+
+Loading all but one feature:
+
+ require "big_band"
+ class Example < BigBand :except => :SomeFeature
+ # Yay, all but BigBand::SomeFeature!
+ end
+
+Or just your favorite feature without you subclassing Sinatra::Base manually:
+
+ require "sinatra"
+ require "big_band/some_feature"
+ Sinatra::Application.register BigBand::SomeFeature
+ # Yay, BigBand::SomeFeature!
+
+== Extensions
+
+=== AdvancedRoutes
+
+AdvancedRoutes makes routes first class objects in Sinatra:
+
+ require "sinatra"
+ require "big_band"
+
+ admin_route = get "/admin" do
+ administrate_stuff
+ end
+
+ before do
+ # Let's deactivate the route if we have no password file.
+ if File.exists? "admin_password"
+ admin_route.activate
+ else
+ admin_route.deactivate
+ end
+ end
+
+ first_route = get "/:name" do
+ # stuff
+ end
+
+ other_route = get "/foo_:name" do
+ # other stuff
+ end
+
+ # Unfortunatly first_route will catch all the requests other_route would
+ # have gotten, since it has been defined first. But wait, we can fix this!
+ other_route.promote
+
+=== BasicExtensions
+
+Basic Sinatra extension (mainly extending Sinatra's standard methods, like set or register).
+Also it features a more advanced path guessing than Sinatra::Base.
+Normally you do not have to register this module manually, as the other extensions will do so
+if necessary.
+
+=== Compass
+
+Integrates the Compass stylesheet framework with Sinatra.
+
+Usage without doing something:
+
+ require "big_band"
+ class Foo < BigBand; end
+
+If you create a directory called views/stylesheets and place your
+sass files in there, there you go. Just call stylesheet(name) form
+your view to get the correct stylesheet tag. The URL for your
+stylesheets will be /stylesheets/:name.css.
+
+Of course you can use any other setup. Say, you want to store your
+stylesheets in views/css and want the URL to be /css/:name.css:
+
+ class Foo < BigBand
+ get_compass("css")
+ end
+
+But what about more complex setups?
+
+ class Foo < BigBand
+ set :compass, :sass_dir => "/foo/bar/blah"
+ get_compass("/foo/:name.css") do
+ compass :one_stylesheet
+ end
+ end
+
+Note that already generated routes will be deactivated by calling
+get_compass again.
+
+=== MoreServer
+
+Adds more servers to Sinatra::Base#run! (currently unicorn and rainbows).
+
+=== Reloader
+
+Advanced reloader for sinatra. Reloads only files that have changed and automatically
+detects orphaned routes that have to be removed. Files defining routes will be added
+to the reload list per default. Avoid reloading with dont_reload. Add other files to
+the reload list with also_reload.
+
+Usage:
+
+ require "big_band"
+ class Foo < Sinatra::Base
+ configure(:development) do
+ register BigBand::Reloader
+ also_reload "app/models/*.rb"
+ dont_reload "lib/**/*.rb"
+ end
+ end
+
+=== WebInspector
+
+Documentation
+
+== Installation
+
+Using RubyGems:
+
+ gem install big_band -s http://gemcutter.org
36 README.rdoc.erb
@@ -0,0 +1,36 @@
+Note: This is in some early stage, so use on your own risk. Commits welcome.
+
+= BigBand
+<%= docstring %>
+
+
+== Extensions
+<% extensions.each do |extension| %>
+
+=== <%= extension.name %>
+
+<%= extension.docstring %>
+
+<% end %>
+
+== Integration
+<% integration.each do |tool, description| %>
+
+=== <%= tool %>
+
+<%= description %>
+
+<% end %>
+
+== Running specs
+
+ rake spec
+
+== Generating documentation
+
+ rake doc
+
+== LICENSE
+(MIT/BSD-stlye license, compatible with Ruby license and GPL)
+
+<%= File.read("LICENSE") %>
76 Rakefile
@@ -0,0 +1,76 @@
+require "erb"
+require "spec/rake/spectask"
+require "rake/clean"
+require "rake/rdoctask"
+require "monkey-lib"
+require "yard"
+
+$LOAD_PATH.unshift __FILE__.dirname / "lib"
+
+require "big_band/integration/yard"
+
+task :default => :spec
+task :test => :spec
+task :clobber => "doc:clobber_rdoc"
+
+CLOBBER << "README.rdoc"
+
+def yard(files)
+ YARD::Registry.load(Dir[files], true)
+ YARD::Registry.resolve(false, "BigBand")
+end
+
+def generate_readme(target = "README.rdoc", template = "README.rdoc.erb")
+ # HACK: loading other libraries later, for some strange heisenbug
+ docstring = yard("lib/big_band.rb").docstring
+ begin
+ require "big_band"
+ extensions = BigBand.default_extensions.map { |e| e.respond_to?(:values) ? e.values : e }
+ extensions.flatten!
+ rescue LoadError
+ extensions = Dir["lib/big_band/*.rb"].map { |f| f[13..-4].to_const_string.to_sym }
+ end
+ y = yard("lib/big_band/**.rb")
+ extensions.map! { |e| y.child(e) }
+ File.open(target, "w") do |f|
+ f << ERB.new(File.read(template), nil, "<>").result(binding)
+ end
+end
+
+file "README.rdoc" => ["README.rdoc.erb", "lib/big_band.rb"] do
+ generate_readme
+end
+
+desc "generate documentation"
+task "doc" => "doc:yardoc"
+task "yardoc" => "doc:yardoc"
+task "rdoc" => "doc:yardoc"
+
+namespace :doc do
+
+ task "yardoc" => "readme"
+ task "rdoc" => "readme"
+ task "rerdoc" => "readme"
+
+ desc "generate README.rdoc from source files"
+ task "readme" do |t|
+ generate_readme
+ end
+
+ Rake::RDocTask.new("rdoc") do |rdoc|
+ rdoc.rdoc_dir = 'doc'
+ rdoc.options += %w[--all --inline-source --line-numbers --main README.rdoc --quiet
+ --tab-width 2 --title BigBand --charset UTF-8]
+ rdoc.rdoc_files.add ['*.{rdoc,rb}', '{config,lib,routes}/**/*.rb']
+ end
+
+ YARD::Rake::YardocTask.new("yardoc") do |t|
+ t.options = %w[--main README.rdoc]
+ end
+
+end
+
+Spec::Rake::SpecTask.new('spec') do |t|
+ t.spec_opts = %w[--options spec/spec.opts]
+ t.spec_files = Dir.glob 'spec/**/*_spec.rb'
+end
25 big_band.gemspec
@@ -0,0 +1,25 @@
+SPEC = Gem::Specification.new do |s|
+
+ s.name = "big_band"
+ s.version = "0.1.0"
+ s.date = "2009-11-01"
+ s.author = "Konstantin Haase"
+ s.email = "konstantin.mailinglists@googlemail.com"
+ s.homepage = "http://github.com/rkh/big_band"
+ s.platform = Gem::Platform::RUBY
+ s.summary = "Collection of Sinatra extensions and sinatra integration for common tools like Rake, YARD and Monk."
+ s.files = Dir.glob "{{lib,spec}/**/*,*.{rdoc,erb,gemspec},Rakefile},LICENSE"
+ s.require_paths = ['lib']
+ s.has_rdoc = true
+ s.rdoc_options = %w[--all --inline-source --line-numbers --main README.rdoc --quiet
+ --tab-width 2 --title BigBand --charset UTF-8]
+
+ s.add_dependency 'sinatra', '>= 0.9.4'
+ s.add_dependency 'monkey-lib', '>= 0.1.6'
+ s.add_dependency 'compass', '>= 0.8.17'
+ s.add_dependency 'yard', '~> 0.2.3.5'
+
+ s.specification_version = 2 if s.respond_to? :specification_version=
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+
+end
206 lib/big_band.rb
@@ -0,0 +1,206 @@
+require "sinatra/base"
+require "extlib/string"
+
+# BigBand is a collection of Sinatra extensions and offers better sinatra integration for common
+# tools like Rake, YARD and Monk.
+#
+# == Usage
+#
+# Using all BigBand features:
+#
+# require "big_band"
+# class Example < BigBand
+# # Yay, BigBand!
+# end
+#
+# Or for the lazy folks (read: you would don't subclass Sinatra::Base on your own):
+#
+# require "sinatra"
+# require "big_band"
+# # Yay, BigBand!
+#
+# Using just your favorite BigBand features:
+#
+# require "big_band"
+# class Example < Sinatra::Base
+# register BigBand::SomeFeature
+# # Yay, BigBand::SomeFeature!
+# end
+#
+# Or, if you like a more handy syntax:
+#
+# require "big_band"
+# class Example < BigBand :SomeFeature, MyStuff::Extension, :development => :DevelopmentOnlyFeature
+# # Yay, BigBand::SomeFeature!
+# # Yay, MyStuff::Extension!
+# # Yay, BigBand::DevelopmentOnlyFeature, if this is development mode!
+# end
+#
+# Loading all but one feature:
+#
+# require "big_band"
+# class Example < BigBand :except => :SomeFeature
+# # Yay, all but BigBand::SomeFeature!
+# end
+#
+# Or just your favorite feature without you subclassing Sinatra::Base manually:
+#
+# require "sinatra"
+# require "big_band/some_feature"
+# Sinatra::Application.register BigBand::SomeFeature
+# # Yay, BigBand::SomeFeature!
+class BigBand < Sinatra::Base
+
+ # Classes generated by BigBand.generate_class will be extended
+ # with this class.
+ module Generated
+
+ attr_reader :big_band_extensions, :big_band_constructor
+
+ # Adds extensions to subclass
+ def inherited(klass)
+ super
+ BigBand.load_extensions(klass, *big_band_extensions)
+ end
+
+ # Use Generated#name for inspection.
+ def inspect
+ name
+ end
+
+ # Nice output for inspect and friends:
+ # foo = Class.new BigBand(:SomeExtension)
+ # foo.name # => BigBand(:SomeExtension)
+ # Foo = foo
+ # foo.name # => Foo
+ def name
+ real_name = super
+ real_name.empty? ? big_band_constructor : real_name
+ end
+
+ end
+
+ extend Generated
+
+ # Extensions to load.
+ def self.big_band_extensions
+ default_extensions
+ end
+
+ # Generates a class for the given extensions. Note that this class is ment to be
+ # subclassed rather than used directly. Given extensione will only be available for
+ # subclasses.
+ #
+ # class Foo < BigBand.generate_class(:except => :SomeExtension)
+ # end
+ def self.generate_class(*options)
+ @generated_classes ||= {[] => BigBand}
+ @generated_classes[options] ||= Class.new(Sinatra::Base) do
+ extend BigBand::Generated
+ @big_band_extensions = options
+ @big_band_constructor = "BigBand(#{options.map { |o| o.inspect}.join ", "})"
+ end
+ end
+
+ # Adds extensions to a Sinatra application:
+ #
+ # class MyApp < Sinatra::Base
+ # end
+ #
+ # BigBand.load_extensions MyApp, :SomeExtension, :development => :AnotherExtension
+ def self.load_extensions(klass, *extensions)
+ extensions = default_extensions if extensions.empty?
+ extensions.flatten.each do |extension|
+ if extension.respond_to? :each_pair
+ extension.each_pair do |key, value|
+ values = [value].flatten
+ case key
+ when :production, :test, :development
+ klass.configure(key) { BigBand.load_extensions(klass, *values) }
+ when :except
+ exts = @nonenv_extensions.reject { |e| values.include? e }
+ exts << @env_extensions.inject({}) do |acceptet, (env, list)|
+ accepted.merge env => list.reject { |e| values.include? e }
+ end
+ load_extensions(klass, *exts)
+ else raise ArgumentError, "unknown key #{key.inspect}"
+ end
+ end
+ else
+ klass.register module_for(extension)
+ end
+ end
+ end
+
+ # Returns the module for a given extension identifier:
+ #
+ # BigBand.module_for :BasicExtension # => BigBand::BasicExtension
+ # BigBand.module_for Array # => Array
+ # BigBand.module_for "Foo::Bar" # => BigBand::Foo::Bar or Foo::Bar or an exception
+ def self.module_for(extension)
+ case extension
+ when Module then extension
+ when String then extension.split("::").inject(self) { |klass, name| klass.const_get name }
+ when Symbol then const_get(extension)
+ end
+ end
+
+ # Default extensions that will be used whenever you subclass BigBand. You can also use this to create
+ # your own extension collection:
+ #
+ # class MyExtensions < BigBand(:except => :ExtensionIDontLike)
+ # default_extension FunkyExtension, :development => DevExtension
+ # end
+ #
+ # Note: If given a string or symbol, it will also try to setup an autoloader:
+ #
+ # MyExtensions.default_extensions :Foo
+ #
+ # Will try to autoload MyExtensions::Foo from "my_extensions/foo" if necessary.
+ def self.default_extensions(*extensions)
+ return @default_extensions if @default_extensions and extensions.empty?
+ @nonenv_extensions ||= []
+ @env_extensions ||= {:development => []}
+ autoload_list = []
+ extensions.each do |extension|
+ if extension.respond_to? :each_pair
+ extension.each_pair do |env, exts|
+ (@env_extensions[env] ||= []).push(*exts)
+ autoload_list.push(*exts)
+ end
+ else
+ @nonenv_extensions.push(*extension)
+ autoload_list.push(*extension)
+ end
+ end
+ autoload_list.each do |ext|
+ next if ext.is_a? Module
+ autoload ext, File.join(self.name.to_const_path, ext.to_s.to_const_path)
+ end
+ @default_extensions = [@nonenv_extensions, @env_extensions].flatten
+ end
+
+ default_extensions :AdvancedRoutes, :BasicExtensions, :Compass, :MoreServer,
+ :development => [:Reloader, :WebInspector]
+
+end
+
+# Shorthand for BigBand.generate_class
+def BigBand(*options)
+ BigBand.generate_class(*options)
+end
+
+module Sinatra
+ module Delegator
+ # Hooks into Sinatra to allow easy integration with "require 'sinatra'".
+ def self.included(klass)
+ BigBand.inherited(Sinatra::Application)
+ Sinatra::Application.extensions.each do |ext|
+ delegate(*ext.delegations) if ext.respond_to? :delegations
+ end
+ end
+ end
+end
+
+# If require "sinatra" came before require "big_band" Sinatra::Delegator.included has not been called.
+Sinatra::Delegator.included(self) if is_a? Sinatra::Delegator
102 lib/big_band/advanced_routes.rb
@@ -0,0 +1,102 @@
+require "sinatra/base"
+
+class BigBand < Sinatra::Base
+
+ # AdvancedRoutes makes routes first class objects in Sinatra:
+ #
+ # require "sinatra"
+ # require "big_band"
+ #
+ # admin_route = get "/admin" do
+ # administrate_stuff
+ # end
+ #
+ # before do
+ # # Let's deactivate the route if we have no password file.
+ # if File.exists? "admin_password"
+ # admin_route.activate
+ # else
+ # admin_route.deactivate
+ # end
+ # end
+ #
+ # first_route = get "/:name" do
+ # # stuff
+ # end
+ #
+ # other_route = get "/foo_:name" do
+ # # other stuff
+ # end
+ #
+ # # Unfortunatly first_route will catch all the requests other_route would
+ # # have gotten, since it has been defined first. But wait, we can fix this!
+ # other_route.promote
+ module AdvancedRoutes
+
+ class Route
+ attr_reader :app, :verbs, :pattern, :keys, :conditions, :block
+
+ # This will be called by Sinatra::Base#routes
+ def initialize(app, verbs, signature)
+ @app, @verbs, (@pattern, @keys, @conditions, @block) = app, [verbs].flatten, signature
+ end
+
+ # The routes signature. Should be uniq for a verb/app.
+ def signature
+ [pattern, keys, conditions, block]
+ end
+
+ # Whether or not a route currently is part of the routes stack.
+ def active?
+ verbs.all? { |verb| app.routes[verb].include? signature }
+ end
+
+ # Adds a route to the routes stack.
+ def activate(at_top = false)
+ verbs.each do |verb|
+ next if app.routes[verb].include? signature
+ invoke_hook(:route_added, verb, path, block)
+ meth = at_top ? :unshift : :pop
+ (routes[verb] ||= []).send(meth, signature)
+ end
+ self
+ end
+
+ # Removes a route from the routes stack.
+ def deactivate
+ verbs.each do |verb|
+ next unless app.routes[verb].include? signature
+ invoke_hook(:route_removed, verb, path, block)
+ (routes[verb] ||= []).delete(signature)
+ end
+ self
+ end
+
+ # Moves a route to the top of the routes stack.
+ # If the optional parameter is false, route will be moved to the bottom instead.
+ def promote(at_top = true)
+ deactivate.activate(at_top)
+ end
+
+ def /(path)
+ pattern.to_s / path
+ end
+
+ end
+
+ module ClassMethods
+ def get(path, opts={}, &block)
+ super.tap { |r| r.verbs.replace ['GET', 'HEAD'] }
+ end
+ def route(verb, path, options={}, &block)
+ path = path.pattern if path.respond_to? pattern
+ Route.new self, verb, super(verb, path, options={}, &block)
+ end
+ end
+
+ def self.registered(klass)
+ klass.extend ClassMethods
+ end
+
+ end
+end
183 lib/big_band/basic_extensions.rb
@@ -0,0 +1,183 @@
+require "sinatra/base"
+require "monkey-lib"
+
+class BigBand < Sinatra::Base
+
+ CALLERS_TO_IGNORE = (class << Sinatra::Base; CALLERS_TO_IGNORE; end)
+ CALLERS_TO_IGNORE << /\/big_band(\/.*)?\.rb$/
+
+ # Basic Sinatra extension (mainly extending Sinatra's standard methods, like set or register).
+ # Also it features a more advanced path guessing than Sinatra::Base.
+ # Normally you do not have to register this module manually, as the other extensions will do so
+ # if necessary.
+ module BasicExtensions
+ module ClassMethods
+
+ attr_writer :root, :guessed_root
+
+ # More advanced set:
+ # - Adds set_#{key} and set_value hooks to set.
+ # - Merges the old value with the new one, if both are hashes:
+ # set :haml, :format => :html5, :escape_html => true
+ # set :haml, :excape_html => false
+ # haml # => { :format => :html5, :escape_html => false }
+ # - Allowes passing a block:
+ # set(:foo) { Time.now }
+ # - Defines a helper to access #{key} and #{key}? unless a helper/method with that name already exists.
+ def set(key, value = self, &block)
+ # FIXME: refactor, refactor, refactor
+ if block_given?
+ raise ArgumentError, "both a value and a block given" if value
+ value = block
+ end
+ symbolized = (key.to_sym if key.respond_to? :to_sym)
+ old_value = (send(symbolized) if symbolized and respond_to? symbolized)
+ value = old_value.merge value if value.is_a? Hash and old_value.is_a? Hash
+ super(key, value)
+ if symbolized
+ methods = instance_methods.map { |m| m.to_s }
+ define_method(key) { self.class.send(key) } unless methods.include? key.to_s
+ define_method("#{key}?") { self.class.send("#{key}?") } unless methods.include? "#{key}?"
+ end
+ # HACK: Sinatra::Base.set uses recursion and in the final step value always
+ # is a Proc. Also, if value is a Proc no step ever follows. I abuse this to
+ # invoke the hooks only once per set.
+ if value.is_a? Proc
+ invoke_hook "set_#{key}", self
+ invoke_hook :set_value, self, key
+ end
+ self
+ end
+
+ # More advacend register:
+ # - If an exntesion is registered twice, the registered hook will only be called once.
+ def register(*extensions, &block)
+ extensions.reject! { |e| self.extensions.include? e }
+ super(*extensions, &block)
+ end
+
+ # Short hand so you can skip those ugly File.expand_path(File.join(File.dirname(__FILE__), ...))
+ # lines.
+ def root_path(*args)
+ relative = File.join(*args)
+ return relative if relative.expand_path == relative
+ root.expand_path.join(relative)
+ end
+
+ # Like root_path, but does return an array instead of a string. Optionally takes a block that will
+ # be called for each entry once.
+ #
+ # Example:
+ # class Foo < BigBand
+ # root_glob("app", "{models,views,controllers}", "*.rb") { |file| load file }
+ # end
+ def root_glob(*args, &block)
+ Dir.glob(root_path(*args), &block)
+ end
+
+ # Whether or not to start a webserver.
+ def run?
+ @run ||= true
+ @run and !@running and app_file? and $0.expand_path == app_file.expand_path
+ end
+
+ # Disable automatically running this class as soon it is subclassed.
+ def inherited
+ super
+ @run = false
+ end
+
+ # The application's root directory. BigBand will guess if missing.
+ def root
+ return ".".expand_path unless app_file?
+ return @root if @root
+ @guessed_root ||= begin
+ dir = app_file.expand_path.dirname
+ if dir.basename == "lib" and not (dir / "lib").directory?
+ dir.dirname
+ else
+ dir
+ end
+ end
+ end
+
+ # Returns true if the #root is known.
+ def root?
+ !!@root || app_file?
+ end
+
+ # Option parser for #run!
+ def run_option_parser
+ @run_option_parser ||= begin
+ require 'optparse'
+ OptionParser.new do |op|
+ op.on('-x') { set :lock, true }
+ op.on('-e env') { |val| set :environment, val.to_sym }
+ op.on('-s server') { |val| set :server, val }
+ op.on('-p port') { |val| set :port, val.to_i }
+ end
+ end
+ end
+
+ # Extended #run!, offers an extandable option parser for
+ # BigBand with the same standard options as the one of
+ # Sinatra#Default (see #run_option_parser).
+ def run!(options = {})
+ run_option_parser.parse!(ARGV.dup) unless ARGV.empty?
+ @running = true
+ super(options)
+ end
+
+ end
+
+ module InstanceMethods
+
+ # Extended #sass: Sets content_type to css if not already set.
+ def sass(*args)
+ content_type 'text/css', :charset => 'utf-8' unless response['Content-Type']
+ super
+ end
+
+ # See BigBand::BasicExtentions::ClassMethods#root_path
+ def root_path(*args)
+ self.class.root_path(*args)
+ end
+
+ # See BigBand::BasicExtentions::ClassMethods#root_path
+ def root_glob(*args, &block)
+ self.class.root_glob(*args, &block)
+ end
+
+ # See BigBand::BasicExtentions::ClassMethods#run?
+ def run?
+ self.class.run?
+ end
+
+ # See BigBand::BasicExtentions::ClassMethods#root
+ def root
+ self.class.root
+ end
+
+ # See BigBand::BasicExtentions::ClassMethods#root?
+ def root?
+ self.class.root
+ end
+
+ end
+
+ def self.registered(klass)
+ klass.set :app_file, klass.caller_files.first.expand_path unless klass.app_file?
+ klass.extend ClassMethods
+ klass.send :include, InstanceMethods
+ klass.set :haml, :format => :html5, :escape_html => true
+ klass.use Rack::Session::Cookie
+ klass.enable :sessions
+ at_exit { run! if run? } unless Sinatra::Application == self
+ end
+
+ def self.set_app_file(klass)
+ klass.guessed_root = nil
+ end
+
+ end
+end
89 lib/big_band/compass.rb
@@ -0,0 +1,89 @@
+require "sinatra/base"
+require "big_band/basic_extensions"
+require "big_band/advanced_routes"
+require "big_band/compass/big_band"
+
+class BigBand < Sinatra::Base
+
+ # Integrates the Compass stylesheet framework with Sinatra.
+ #
+ # Usage without doing something:
+ #
+ # require "big_band"
+ # class Foo < BigBand; end
+ #
+ # If you create a directory called views/stylesheets and place your
+ # sass files in there, there you go. Just call stylesheet(name) form
+ # your view to get the correct stylesheet tag. The URL for your
+ # stylesheets will be /stylesheets/:name.css.
+ #
+ # Of course you can use any other setup. Say, you want to store your
+ # stylesheets in views/css and want the URL to be /css/:name.css:
+ #
+ # class Foo < BigBand
+ # get_compass("css")
+ # end
+ #
+ # But what about more complex setups?
+ #
+ # class Foo < BigBand
+ # set :compass, :sass_dir => "/foo/bar/blah"
+ # get_compass("/foo/:name.css") do
+ # compass :one_stylesheet
+ # end
+ # end
+ #
+ # Note that already generated routes will be deactivated by calling
+ # get_compass again.
+ module Compass
+
+ module ClassMethods
+ attr_reader :compass_route
+ def get_compass(path, &block)
+ if path
+ block ||= Proc.new do |file|
+ response['Cache-Control'] = 'public' if self.class.production?
+ compass block.call(params[:name]).to_sym
+ end
+ set :compass, :sass_dir => klass.views.join(path) unless compass[:sass_dir] && compass[:sass_dir].directory?
+ end
+ @compass_route.deactivate if @compass_route
+ @compass_route = get(path / ":name.css", &block)
+ end
+ end
+
+ module InstanceMethods
+ def compass(file, options = {})
+ options.merge! Compass.sass_engine_options
+ sass file, options
+ end
+ def stylesheet(*args)
+ raise NotImplementedError, "yet to be implemented"
+ end
+ end
+
+ def self.registered(klass)
+ klass.register BasicExtensions
+ klass.register AdvancedRoutes
+ klass.extend ClassMethods
+ klass.send :include, InstanceMethod
+ klass.set :compass, :root_path => klass.root_path, :output_style => :compact,
+ :sass_dir => klass.views.join("stylesheets")
+ set_app_file(klass) if klass.app_file?
+ end
+
+ def self.set_app_file(klass)
+ klass.set :compass, :root_path => klass.root_path
+ get_compass("stylesheets") if klass.views.join("stylesheets").directory?
+ end
+
+ def self.set_compass(options = {})
+ Compass.configuration do |config|
+ options.each do |option, value|
+ config.send "#{option}=", value
+ end
+ end
+ end
+
+ end
+end
3  lib/big_band/compass/big_band.rb
@@ -0,0 +1,3 @@
+require "compass"
+
+Compass::Frameworks.register 'big_band', File.expand_path(File.dirname(__FILE__))
1  lib/big_band/compass/stylesheets/_big_band.sass
@@ -0,0 +1 @@
+@import big_band/utilities.sass
0  lib/big_band/compass/stylesheets/big_band/_blueprint.sass
No changes.
0  lib/big_band/compass/stylesheets/big_band/_utilities.sass
No changes.
28 lib/big_band/integration.rb
@@ -0,0 +1,28 @@
+unless defined? Sinatra::Base
+ module Sinatra # :nodoc:
+ class Base # :nodoc:
+ end
+ end
+end
+
+class BigBand < Sinatra::Base
+ module Integration
+ GLOBBER = "{lib/**,app/**,routes/**,models/**,views/**,controllers/**,.}/*.rb"
+
+ def self.routes_for(source)
+ case source
+ when Class
+ source.routes
+ when String
+ require "big_band/integration/yard"
+ YARD::Registry.load(Dir[files], true)
+ YARD::Handlers::Sinatra::AbstractRouteHandler.routes.inject({}) do |routes, route_object|
+ routes.merge route_object.http_verb => route_object.http_path
+ end
+ else
+ raise ArgumentError, "cannot retrieve routes for #{source.inspect}"
+ end
+ end
+
+ end
+end
13 lib/big_band/integration/monk.rb
@@ -0,0 +1,13 @@
+require "monk"
+require "big_band/integration"
+
+class Monk < Thor
+
+ desc "routes [FILES=#{GLOBBER.inspect}]", "lists all routes"
+ def routes(files = GLOBBER)
+ BigBand::Integration.routes_for(files).each do |verb, path|
+ say_status verb, path
+ end
+ end
+
+end
29 lib/big_band/integration/rake.rb
@@ -0,0 +1,29 @@
+require "big_band/integration"
+require "rake"
+require "rake/tasklib"
+
+module BigBand::Integration
+ module Rake
+
+ class RoutesTask < ::Rake::TaskLib
+ attr_accessor :source, :name
+
+ def initialize(name = :routes)
+ @name = name
+ @source = BigBand::Integration::GLOBBER
+ yield self if block_given
+ define
+ end
+
+ def define
+ desc "Lists routes defined in #{source}"
+ task(name) do
+ BigBand::Integration.routes_for(source).each do |verb, path|
+ puts "#{verb} #{path}"
+ end
+ end
+ end
+
+ end
+ end
+end
15 lib/big_band/integration/rspec.rb
@@ -0,0 +1,15 @@
+require "big_band/integration"
+require "spec"
+require "webrat"
+require "rack/test"
+
+Webrat.configure { |config| config.mode = :sinatra }
+
+module BigBand::TestMethods
+end
+
+Spec::Runner.configure do |conf|
+ conf.include Webrat::Methods
+ conf.include Rack::Test::Methods
+ conf.include BigBand::TestMethods
+end
74 lib/big_band/integration/yard.rb
@@ -0,0 +1,74 @@
+require "yard"
+
+module YARD
+
+ module CodeObjects
+ class RouteObject < MethodObject
+ attr_accessor :http_verb, :http_path
+ end
+ end
+
+ module Handlers
+
+ # Displays Sinatra routes in YARD documentation.
+ # Can also be used to parse routes from files without executing those files.
+ module Sinatra
+
+ # Logic both handlers have in common.
+ module AbstractRouteHandler
+ def self.routes
+ @routes ||= []
+ end
+ def process
+ path = http_path
+ path = $1 if path =~ /^"(.*)"$/
+ method_name = http_verb.upcase << " " << path
+ @route = CodeObjects::RouteObject.new(namespace, method_name, scope) do |o|
+ o.visibility = "public"
+ o.source = statement.source
+ o.signature = method_name
+ o.explicit = true
+ o.scope = scope
+ o.add_file(parser.file, statement.line)
+ o.docstring = statement.comments
+ o.http_verb = http_verb.upcase
+ o.http_path = path
+ end
+ AbstractRouteHandler.routes << @route
+ register @route
+ end
+ end
+
+ # Route handler for YARD's source parser.
+ class RouteHandler < Ruby::Base
+ include AbstractRouteHandler
+ handles method_call(:get)
+ handles method_call(:post)
+ handles method_call(:put)
+ handles method_call(:delete)
+ handles method_call(:head)
+ def http_verb
+ statement.method_name(true).to_s
+ end
+ def http_path
+ statement.parameters.first.source
+ end
+ end
+
+ # Route handler for YARD's legacy parser.
+ module Legacy
+ class RouteHandler < Ruby::Legacy::Base
+ include AbstractRouteHandler
+ handles /\A(get|post|put|delete|head)[\s\(].*/m
+ def http_verb
+ statement.tokens.first.text
+ end
+ def http_path
+ statement.tokens[2].text
+ end
+ end
+ end
+
+ end
+ end
+end
14 lib/big_band/more_server.rb
@@ -0,0 +1,14 @@
+require "sinatra/base"
+
+class BigBand < Sinatra::Base
+ # Adds more servers to Sinatra::Base#run! (currently unicorn and rainbows).
+ module MoreServer
+ autoload :Unicorn, "big_band/more_server/unicorn"
+ autoload :Rainbows, "big_band/more_server/rainbows"
+ def self.registered(klass)
+ Rack::Handler.register "unicorn", "BigBand::MoreServer::Unicorn"
+ Rack::Handler.register "rainbows", "BigBand::MoreServer::Rainbows"
+ klass.server.unshift "rainbows", "unicorn"
+ end
+ end
+end
13 lib/big_band/more_server/rainbows.rb
@@ -0,0 +1,13 @@
+require "big_band/more_server/unicorn"
+require "rainbows"
+
+class BigBand < Sinatra::Base
+ module MoreServer
+ # Rack Handler to use Rainbows for Sinatra::Base.run!
+ module Rainbows
+ def self.run(app, options = {})
+ BigBand::Rack::Unicorn.run app, options.merge(:Backend => ::Rainbows)
+ end
+ end
+ end
+end
28 lib/big_band/more_server/unicorn.rb
@@ -0,0 +1,28 @@
+require "sinatra/base"
+require "unicorn"
+require "rack/content_length"
+require "rack/chunked"
+
+class BigBand < Sinatra::Base
+ module MoreServer
+ # Rack Handler to use Unicorn for Sinatra::Base.run!
+ module Unicorn
+ def self.run(app, options={})
+ app = Rack::Builder.new do
+ # TODO: env dependend stuff.
+ use Rack::CommonLogger, $stderr
+ use Rack::ShowExceptions
+ use Rack::Lint
+ run app
+ end.to_app
+ options[:Backend] ||= ::Unicorn
+ options[:Host] ||= ::Unicorn::Const::DEFAULT_HOST
+ options[:Port] ||= ::Unicorn::Const::DEFAULT_PORT
+ options[:listeners] = ["#{options.delete :Host}:#{options.delete :Port}"]
+ server = options.delete(:Backend)::HttpServer.new app, options
+ yield server if block_given?
+ server.start.join
+ end
+ end
+ end
+end
73 lib/big_band/reloader.rb
@@ -0,0 +1,73 @@
+require "sinatra/base"
+require "big_band/basic_extensions"
+
+class BigBand < Sinatra::Base
+
+ # Advanced reloader for sinatra. Reloads only files that have changed and automatically
+ # detects orphaned routes that have to be removed. Files defining routes will be added
+ # to the reload list per default. Avoid reloading with dont_reload. Add other files to
+ # the reload list with also_reload.
+ #
+ # Usage:
+ #
+ # require "big_band"
+ # class Foo < Sinatra::Base
+ # configure(:development) do
+ # register BigBand::Reloader
+ # also_reload "app/models/*.rb"
+ # dont_reload "lib/**/*.rb"
+ # end
+ # end
+ module Reloader
+
+ def self.registered(klass)
+ klass.register BasicExtensions
+ klass.before do
+ if defined? Thread and Thread.list.size > 1 and Thread.respond_to? :exclusive
+ Thread.exclusive { klass.reload_routes }
+ else
+ klass.reload_routes
+ end
+ end
+ end
+
+ def route(verb, path, options={}, &block)
+ file = caller_files.first.expand_path
+ super.tap do
+ signature = signature.signature if signature.respond_to? :signature
+ routes_from(file) << [verb, signature] if File.exist? file
+ end
+ end
+
+ def reload_index
+ @routes_index ||= {}
+ end
+
+ def routes_from(file)
+ (reload_index[file] ||= [File.mtime(file), []]).last
+ end
+
+ def reload_routes
+ reload_index.each do |file, (mtime, old_routes)|
+ next if mtime >= File.mtime(file) or reload_skip_rules.any? { |r| root_glob(r).include? file }
+ old_routes.each { |verb, signature| (routes[verb] ||= []).delete signature }
+ $LOADED_FEATURES.delete file
+ old_routes.clear
+ require file if File.exist? file
+ end
+ end
+
+ def reload_skip_rules
+ @reload_skip_rules ||= []
+ end
+
+ def dont_reload(files)
+ reload_skip_rules << files
+ end
+
+ def also_reload(files)
+ root_glob(files) { |file| routes_from(file) }
+ end
+
+ end
+end
55 lib/big_band/web_inspector.rb
@@ -0,0 +1,55 @@
+require "sinatra/base"
+require "monkey-lib"
+
+class BigBand < Sinatra::Base
+
+ # Documentation
+ module WebInspector
+
+ attr_reader :middleware
+
+ class Middleware < Sinatra::Base
+ use_in_file_templates! __FILE__
+ register BasicExtensions
+ set :sinatra_app, BigBand
+ set :app_file, __FILE__
+ set :views, root_path("web_inspector")
+ get "/__inspect__" do
+ haml :inspect, {},
+ :title => sinatra_app.name + ".inspect",
+ :name => sinatra_app.name,
+ :extensions => sinatra_app.extensions,
+ :routes => sinatra_app.routes,
+ :middleware => sinatra_app.middleware
+ end
+ end
+
+ def self.registered(klass)
+ klass.use(Middleware) { |m| m.sinatra_app = klass }
+ end
+
+ end
+end
+
+__END__
+
+@@layout
+!!!
+%html
+ %head
+ %meta{:charset=>"utf-8"}/
+ %title= title
+ %body
+ %header
+ %h1= title
+ Generated on
+ %date= Time.now
+ %article
+ =yield
+ %footer
+ powered by
+ %a{:href => "http://www.sinatrarb.com"} Sinatra
+ and
+ %a{:href => "http://github.com/rkh/big_band"} BigBand
+
+@@inspect
6 lib/big_bang.rb
@@ -0,0 +1,6 @@
+require "big_band"
+
+unless defined? BigBang
+ warn "It is called BigBand, not BigBang!"
+ BigBang = BigBand
+end
4 spec/big_band/advanced_routes_spec.rb
@@ -0,0 +1,4 @@
+require File.expand_path(__FILE__ + "../../../spec_helper.rb")
+
+describe BigBand::AdvancedRoutes do
+end
20 spec/big_band/basic_extensions_spec.rb
@@ -0,0 +1,20 @@
+require File.expand_path(__FILE__ + "../../../spec_helper.rb")
+
+describe BigBand::BasicExtensions do
+
+ describe "set" do
+ it "adds hooks to Sinatra::Base#set"
+ it "allows passing a block"
+ it "merges hash values"
+ end
+
+ describe "register" do
+ it "registers an extension only once"
+ end
+
+ describe "sass" do
+ it "should set content type for css, if none is set"
+ it "should not set any content type if a content type is already defined"
+ end
+
+end
4 spec/big_band/compass_spec.rb
@@ -0,0 +1,4 @@
+require File.expand_path(__FILE__ + "../../../spec_helper.rb")
+
+describe BigBand::Compass do
+end
4 spec/big_band/more_server_spec.rb
@@ -0,0 +1,4 @@
+require File.expand_path(__FILE__ + "../../../spec_helper.rb")
+
+describe BigBand::MoreServer do
+end
4 spec/big_band/reloader_spec.rb
@@ -0,0 +1,4 @@
+require File.expand_path(__FILE__ + "../../../spec_helper.rb")
+
+describe BigBand::Reloader do
+end
4 spec/spec.opts
@@ -0,0 +1,4 @@
+--colour
+--format progress
+--loadby mtime
+--reverse
5 spec/spec_helper.rb
@@ -0,0 +1,5 @@
+ENV['RACK_ENV'] = 'test'
+
+$LOAD_PATH.unshift File.expand_path(__FILE__ + "../../lib")
+require "big_band"
+require "big_band/integration/rspec"
Please sign in to comment.
Something went wrong with that request. Please try again.