bang-bang 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.gitignore +2 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +97 -0
  5. data/README.markdown +42 -0
  6. data/Rakefile +2 -0
  7. data/bang-bang.gemspec +38 -0
  8. data/lib/bang-bang/controller.rb +126 -0
  9. data/lib/bang-bang/env_methods.rb +13 -0
  10. data/lib/bang-bang/plugins/directory_first_sort.rb +14 -0
  11. data/lib/bang-bang/plugins.rb +29 -0
  12. data/lib/bang-bang/service.rb +176 -0
  13. data/lib/bang-bang/version.rb +3 -0
  14. data/lib/bang-bang/views.rb +81 -0
  15. data/lib/bang-bang.rb +130 -0
  16. data/spec/bang-bang/controller_spec.rb +36 -0
  17. data/spec/bang-bang/service_spec.rb +35 -0
  18. data/spec/bang-bang/views_spec.rb +46 -0
  19. data/spec/fixture-app/app.rb +30 -0
  20. data/spec/fixture-app/services/authentication/app/controllers/authentication.rb +6 -0
  21. data/spec/fixture-app/services/authentication/app/presenters/index.html.ms.rb +5 -0
  22. data/spec/fixture-app/services/authentication/app/templates/index.html.ms +4 -0
  23. data/spec/fixture-app/services/authentication/init.rb +1 -0
  24. data/spec/fixture-app/services/authentication/public/javascripts/foo.js +2 -0
  25. data/spec/spec_helper.rb +45 -0
  26. data/spec/spec_helpers/example_group.rb +47 -0
  27. data/spec/spec_suite.rb +3 -0
  28. data/vendor/superhash/InstalledFiles +1 -0
  29. data/vendor/superhash/README +5 -0
  30. data/vendor/superhash/RELEASE-NOTES +9 -0
  31. data/vendor/superhash/config.save +12 -0
  32. data/vendor/superhash/examples/attributed-node.rb +83 -0
  33. data/vendor/superhash/examples/class-superhash.rb +26 -0
  34. data/vendor/superhash/examples/state-object.rb +95 -0
  35. data/vendor/superhash/install.rb +1015 -0
  36. data/vendor/superhash/lib/superhash.rb +454 -0
  37. data/vendor/superhash/test/test.rb +124 -0
  38. metadata +235 -0
data/lib/bang-bang.rb ADDED
@@ -0,0 +1,130 @@
1
+ dir = File.dirname(__FILE__)
2
+ Dir[File.expand_path("#{dir}/../vendor/*/lib")].each do |path|
3
+ $LOAD_PATH.unshift(path)
4
+ end
5
+ require 'sinatra/base'
6
+ require 'mustache'
7
+ require 'yajl'
8
+ require 'active_support/all'
9
+ require 'addressable/uri'
10
+ require "named-routes"
11
+ require "superhash"
12
+ require "#{dir}/bang-bang/version"
13
+ require "#{dir}/bang-bang/env_methods"
14
+
15
+ module BangBang
16
+ def self.included(mod)
17
+ mod.extend(ClassMethods)
18
+ end
19
+
20
+ module ClassMethods
21
+ attr_accessor :controller, :application_name, :named_routes, :stderr_dir, :stdout_dir, :root_dir, :views_class
22
+ alias_method :uris, :named_routes
23
+ delegate :define_routes, :to => :controller
24
+
25
+ include ::BangBang::EnvMethods
26
+
27
+ def init(params={})
28
+ self.controller = params[:controller] || raise(ArgumentError, "You must provide an :controller param")
29
+ self.application_name = params[:application_name] || raise(ArgumentError, "You must provide an :application_name param")
30
+ self.root_dir = params[:root_dir] || raise(ArgumentError, "You must provide a :root_dir param")
31
+ self.named_routes = params[:named_routes] || raise(ArgumentError, "You must provide a :named_routes param")
32
+ self.views_class = params[:views_class] || raise(ArgumentError, "You must provide a :views_class param")
33
+ self.controller.config = self
34
+
35
+ plugins.init
36
+ end
37
+
38
+ def app
39
+ @app ||= Rack::Builder.new do
40
+ run controller
41
+ end.to_app
42
+ end
43
+
44
+ def register_service(path, &block)
45
+ unless service_dirs.include?(path)
46
+ service = Service.new(path)
47
+ services << service
48
+ service.init(&block)
49
+ end
50
+ end
51
+
52
+ def service_dirs
53
+ services.map do |service|
54
+ service.root_dir
55
+ end
56
+ end
57
+
58
+ def services
59
+ @services ||= []
60
+ end
61
+
62
+ def services_by_url_prefix
63
+ services.group_by do |service|
64
+ service.url_prefix
65
+ end
66
+ end
67
+
68
+ def services_by_root_dir
69
+ services.inject({}) do |memo, service|
70
+ memo[service.root_dir] = service
71
+ memo
72
+ end
73
+ end
74
+
75
+ def stderr_logger
76
+ @stderr_logger ||= Logger.new(stderr_dir)
77
+ end
78
+
79
+ def stdout_logger
80
+ @stdout_logger ||= Logger.new(stdout_dir)
81
+ end
82
+
83
+ def plugins
84
+ @plugins ||= ::BangBang::Plugins::Set.new(self)
85
+ end
86
+
87
+ def lib_dir
88
+ File.join(root_dir, "lib")
89
+ end
90
+
91
+ def stylesheets_dirs
92
+ service_subdirectory_dirs "app/stylesheets"
93
+ end
94
+
95
+ def vendor_dir
96
+ File.join(root_dir, "vendor")
97
+ end
98
+
99
+ def services_dir
100
+ File.join(root_dir, "services")
101
+ end
102
+
103
+ def service_subdirectory_dirs(relative_directory)
104
+ service_dirs.flatten.map do |service_path|
105
+ full_path = File.join(service_path, relative_directory)
106
+ full_path if File.directory?(full_path)
107
+ end.compact
108
+ end
109
+
110
+ def stderr_dir
111
+ "#{root_dir}/log/#{application_name}.#{rack_env}.stderr.log"
112
+ end
113
+
114
+ def stdout_dir
115
+ "#{root_dir}/log/#{application_name}.#{rack_env}.stdout.log"
116
+ end
117
+
118
+ def remove_generated_files
119
+ Dir["#{root_dir}/**/public/**/*.generated"].each do |generated_path|
120
+ FileUtils.rm_f(File.join(File.dirname(generated_path), File.basename(generated_path, ".generated")))
121
+ FileUtils.rm_f(generated_path)
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ require "#{dir}/bang-bang/controller"
128
+ require "#{dir}/bang-bang/service"
129
+ require "#{dir}/bang-bang/views"
130
+ require "#{dir}/bang-bang/plugins"
@@ -0,0 +1,36 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+
3
+ module BangBang
4
+ describe Controller do
5
+ describe "GET /authentication/error-page" do
6
+ context "when there is not a rack.logger" do
7
+ it "responds with a 500" do
8
+ any_instance_of(FixtureApp::Controller) do |controller|
9
+ stub.proxy(controller).env do |env|
10
+ env.delete("rack.logger")
11
+ env
12
+ end
13
+ end
14
+ get uris.authentication_error_page
15
+
16
+ response.status.should == 500
17
+ end
18
+ end
19
+
20
+ context "when there is a rack.logger" do
21
+ it "responds with a 500 and logs the message" do
22
+ message = nil
23
+ any_instance_of(Logger) do |l|
24
+ stub(l).error(is_a(String)) do |*args|
25
+ message = args.first
26
+ end
27
+ end
28
+ get uris.authentication_error_page
29
+
30
+ response.status.should == 500
31
+ message.should include("An Error")
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+
3
+ module BangBang
4
+ describe Service do
5
+ describe "#get_static_file_path" do
6
+ context "when the service has a file matching the given url" do
7
+ it "returns the file path of the static file base on the Service's prefix + file path" do
8
+ authentication_path = "#{FixtureApp.root_dir}/services/authentication"
9
+ service = Service.new(authentication_path).init
10
+ service.url_prefix.should == "/authentication"
11
+ service.get_static_file_path("/authentication/javascripts/foo.js").should ==
12
+ File.join(authentication_path, "/public/javascripts/foo.js")
13
+ end
14
+ end
15
+
16
+ context "when the service does not have a file matching the given url" do
17
+ it "returns nil" do
18
+ authentication_path = "#{FixtureApp.root_dir}/services/authentication"
19
+ service = Service.new(authentication_path).init
20
+ service.get_static_file_path("i-dont-exist").should be_nil
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "#templates_hash" do
26
+ it "returns a hash of all of the template files" do
27
+ authentication_path = "#{FixtureApp.root_dir}/services/authentication"
28
+ service = Service.new(authentication_path).init
29
+
30
+ hash = service.templates_hash
31
+ hash["/authentication/index.html.ms"].should == File.read(File.join(service.templates_dir, "index.html.ms"))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+
3
+ module BangBang
4
+ describe Views do
5
+ describe "#[]" do
6
+ it "returns an object with a render method, which invokes the given method name" do
7
+ view = BangBang::Views.new(Object.new)
8
+ mock(view, "true_car_makes")
9
+
10
+ view["true_car_makes"].render
11
+ end
12
+
13
+ describe "lazily created method" do
14
+ context "when the presenter file exists" do
15
+ it "evals the presenter file (which is responsible for adding the method)" do
16
+ authentication_path = "#{FixtureApp.root_dir}/services/authentication"
17
+ service = Service.new(authentication_path).init
18
+ app_instance = Object.new
19
+ stub(app_instance).services {[service]}
20
+ stub(app_instance).config {FixtureApp}
21
+ views = Class.new(BangBang::Views).new(app_instance)
22
+
23
+ html = views["/authentication/index.html.ms"].call("param1 value")
24
+ html.should include("param1 value")
25
+ doc = Nokogiri::HTML(html)
26
+ doc.at(".child-div").should be_present
27
+ end
28
+ end
29
+
30
+ context "when the presenter file does not exist" do
31
+ it "lazily creates a method that render the mustache template for the given path" do
32
+ app_instance = Object.new
33
+ app_instance.class.send(:define_method, :config) do
34
+ FixtureApp
35
+ end
36
+ views = Class.new(BangBang::Views).new(app_instance)
37
+
38
+ views.respond_to?("/user/pagelets/i-dont-exist").should be_false
39
+ views["/user/pagelets/i-dont-exist"]
40
+ views.respond_to?("/user/pagelets/i-dont-exist").should be_true
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,30 @@
1
+ module FixtureApp
2
+ include ::BangBang
3
+
4
+ def self.app
5
+ @app ||= Rack::Builder.new do
6
+ use Rack::Logger
7
+ run ::FixtureApp::Controller
8
+ end.to_app
9
+ end
10
+
11
+ class Controller < ::BangBang::Controller
12
+ set :dump_errors, false
13
+ end
14
+
15
+ class Views < ::BangBang::Views
16
+ end
17
+
18
+ class Routes < NamedRoutes::Routes
19
+ end
20
+ end
21
+
22
+ FixtureApp.init(
23
+ :controller => FixtureApp::Controller,
24
+ :application_name => "fixture-app",
25
+ :root_dir => File.dirname(__FILE__),
26
+ :named_routes => FixtureApp::Routes,
27
+ :views_class => FixtureApp::Views
28
+ )
29
+
30
+ FixtureApp.register_service("#{File.dirname(__FILE__)}/services/authentication")
@@ -0,0 +1,6 @@
1
+ FixtureApp::Controller.define_routes do
2
+ def authentication_error_page; end
3
+ get path(:authentication_error_page, "/authentication/error-page") do
4
+ raise "An Error"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ mustache "/authentication/index.html.ms" do |param1|
2
+ {
3
+ "param1" => param1
4
+ }
5
+ end
@@ -0,0 +1,4 @@
1
+ <div class="authentication-index">
2
+ <div class="child-div"></div>
3
+ {{param1}}
4
+ </div>
@@ -0,0 +1 @@
1
+ self.url_prefix = "/authentication"
@@ -0,0 +1,2 @@
1
+ function Foo() {
2
+ }
@@ -0,0 +1,45 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.setup
4
+
5
+ dir = File.dirname(__FILE__)
6
+ $LOAD_PATH.unshift(File.expand_path("#{dir}/../lib"))
7
+
8
+ require "rack/test"
9
+
10
+ require "bang-bang"
11
+ require "#{dir}/fixture-app/app"
12
+ require "rack/session/abstract/id"
13
+ require "rack/test"
14
+ require "capybara"
15
+ require "capybara/dsl"
16
+ require "addressable/uri"
17
+
18
+ ARGV.push("-b")
19
+ unless ARGV.include?("--format") || ARGV.include?("-f")
20
+ ARGV.push("--format", "nested")
21
+ end
22
+
23
+ require 'rspec'
24
+ require 'rspec/autorun'
25
+ require 'rr'
26
+ require 'webmock/rspec'
27
+
28
+ ENV["RACK_ENV"] = "test"
29
+
30
+ Dir["#{File.dirname(__FILE__)}/spec_helpers/**/*.rb"].each do |file|
31
+ require file
32
+ end
33
+
34
+ RSpec.configure do |configuration|
35
+ configuration.mock_with :rr
36
+ configuration.filter_run :focus => true
37
+ configuration.run_all_when_everything_filtered = true
38
+ configuration.before do
39
+ Capybara.reset!
40
+
41
+ FixtureApp.views_class = FixtureApp::Views
42
+ end
43
+ end
44
+
45
+ Capybara.app = FixtureApp.app
@@ -0,0 +1,47 @@
1
+ RSpec::Core::ExampleGroup.class_eval do
2
+ include Capybara
3
+
4
+ def app
5
+ FixtureApp::Controller
6
+ end
7
+
8
+ def uris
9
+ @uris ||= FixtureApp::Routes.instance
10
+ end
11
+ alias_method :named_routes, :uris
12
+
13
+ def views
14
+ @views ||= begin
15
+ app_instance = Capybara.app.allocate
16
+ app_instance.send(:initialize)
17
+ app_instance.session_data = session_data
18
+ ::FixtureApp::Views.new(app_instance)
19
+ end
20
+ end
21
+
22
+ [
23
+ :request, :last_request, :response, :last_response, :follow_redirect!,
24
+ :rack_mock_session
25
+ ].each do |method_name|
26
+ class_eval((<<-RUBY), __FILE__, __LINE__+1)
27
+ def #{method_name}(*args, &block)
28
+ page.driver.#{method_name}(*args, &block)
29
+ end
30
+ RUBY
31
+ end
32
+
33
+ [:get, :put, :post, :delete].each do |method_name|
34
+ class_eval((<<-RUBY), __FILE__, __LINE__+1)
35
+ def #{method_name}(*args, &block)
36
+ (res = page.driver.#{method_name}(*args, &block)).tap do
37
+ puts res.errors if res.status == 500
38
+ end
39
+ end
40
+ RUBY
41
+ end
42
+
43
+ def current_path
44
+ Addressable::URI.parse(current_url).path
45
+ end
46
+
47
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/**/*_spec.rb"].each do |file|
2
+ require file
3
+ end
@@ -0,0 +1 @@
1
+ /home/honk/.rvm/rubies/ruby-1.9.2-p0/lib/ruby/site_ruby/1.9.1//superhash.rb
@@ -0,0 +1,5 @@
1
+ To install:
2
+
3
+ ruby install.rb config
4
+ ruby install.rb setup
5
+ ruby install.rb install
@@ -0,0 +1,9 @@
1
+ superhash 0.3
2
+ - Fixed broken #index.
3
+ - Updated for ruby-1.8 and ruby-1.9.
4
+
5
+ superhash 0.2
6
+ - Updated for ruby-1.7.3.
7
+
8
+ superhash 0.1
9
+ - First public release.
@@ -0,0 +1,12 @@
1
+ prefix=/home/honk/.rvm/rubies/ruby-1.9.2-p0
2
+ std-ruby=$prefix/lib/ruby/1.9.1
3
+ site-ruby-common=$prefix/lib/ruby/site_ruby
4
+ site-ruby=$prefix/lib/ruby/site_ruby/1.9.1
5
+ bin-dir=$prefix/bin
6
+ rb-dir=$site-ruby
7
+ so-dir=$prefix/lib/ruby/site_ruby/1.9.1/x86_64-linux
8
+ data-dir=$prefix/share
9
+ ruby-path=/home/honk/.rvm/rubies/ruby-1.9.2-p0/bin/ruby
10
+ ruby-prog=/home/honk/.rvm/rubies/ruby-1.9.2-p0/bin/ruby
11
+ make-prog=make
12
+ without-ext=no
@@ -0,0 +1,83 @@
1
+ require 'superhash'
2
+
3
+ # Example of using SuperHashes inside regular objects (not classes).
4
+ # This example was distilled from the SuperML project.
5
+
6
+ class AttributedNode
7
+
8
+ attr_reader :attributes, :parents, :children
9
+
10
+ # creates a node and gives it a first parent
11
+ def initialize(parent = nil)
12
+ @attributes = SuperHash.new(parent && parent.attributes)
13
+ @parents = parent ? [parent] : []
14
+ @children = []
15
+ end
16
+
17
+ # adds more parents, which will also contribute attributes
18
+ def add_parent(parent)
19
+ unless @parents.include? parent
20
+ @parents << parent
21
+ @attributes.parents << parent.attributes
22
+ end
23
+ self
24
+ end
25
+ protected :add_parent
26
+
27
+ def add_child(child)
28
+ @children << child
29
+ child.add_parent(self)
30
+ self
31
+ end
32
+
33
+ end
34
+
35
+ # This might be the description of a window using some (imaginary)
36
+ # GUI construction library.
37
+
38
+ root = AttributedNode.new
39
+ root.attributes[:type] = :top_window
40
+ root.attributes[:name] = "Feature request window"
41
+ root.attributes[:bg_color] = "gray"
42
+ root.attributes[:font_face] = "Courier"
43
+ root.attributes[:font_size] = 10
44
+
45
+ user_field = AttributedNode.new
46
+ root.add_child(user_field)
47
+ user_field.attributes[:type] = :text_box
48
+ user_field.attributes[:name] = "Username box"
49
+ user_field.attributes[:label] = "Name:"
50
+
51
+ feature_field = AttributedNode.new
52
+ root.add_child(feature_field)
53
+ feature_field.attributes[:type] = :text_box
54
+ feature_field.attributes[:name] = "Feature description box"
55
+ feature_field.attributes[:bg_color] = "white"
56
+ feature_field.attributes[:label] = "Please enter your request below:"
57
+ feature_field.attributes[:font_face] = "Times"
58
+ feature_field.attributes[:font_size] = 12
59
+
60
+
61
+ # So then we have:
62
+
63
+ p user_field.attributes[:bg_color] # ==> "gray"
64
+ p feature_field.attributes[:bg_color] # ==> "white"
65
+
66
+ p user_field.attributes[:font_size] # ==> 10
67
+ p feature_field.attributes[:font_size] # ==> 12
68
+
69
+
70
+ # You could even manage another set of attributes of the same objects using
71
+ # multiple inheritance, such as attributes pertaining to form entry content:
72
+
73
+ form_entry = AttributedNode.new
74
+ form_entry.attributes[:default_contents] = "enter text here"
75
+ form_entry.attributes[:reject_condition] = /[^\w,.;:?!]/
76
+
77
+ form_entry.add_child(user_field)
78
+ user_field.attributes[:default_contents] = "fred"
79
+
80
+ form_entry.add_child(feature_field)
81
+ feature_field.attributes[:reject_condition] = /more like Microsoft/
82
+
83
+ p feature_field.attributes[:default_contents] # ==> "enter text here"
@@ -0,0 +1,26 @@
1
+ require 'superhash'
2
+
3
+ class A
4
+ class_superhash :options
5
+
6
+ options[:foo] = "A foo"
7
+ options[:bar] = "A bar"
8
+
9
+ def options; self.class.options; end
10
+ end
11
+
12
+ class B < A
13
+ options[:foo] = "B foo"
14
+ end
15
+
16
+ p A.options
17
+ p B.options.to_hash
18
+ p B.new.options.to_hash
19
+
20
+ __END__
21
+
22
+ output:
23
+
24
+ {:foo=>"A foo", :bar=>"A bar"}
25
+ {:foo=>"B foo", :bar=>"A bar"}
26
+ {:foo=>"B foo", :bar=>"A bar"}
@@ -0,0 +1,95 @@
1
+ require 'superhash'
2
+
3
+ # Example of using SuperHashes in class instance variables to extend Ruby's
4
+ # inheritance mechanism. This can be expressed automatically using the
5
+ # Class#class_superhash methods defined in superhash.rb.
6
+ #
7
+ # This example is vaguely derived from the RedShift project. Subclasses
8
+ # of StateObject have a set of states that their instances may be in. Each
9
+ # state is mapped to a state action. Subclasses inherit the mapping, and may
10
+ # add to it and override it.
11
+
12
+ class StateObject
13
+
14
+ # class vars and class methods
15
+
16
+ @state_actions = {}
17
+ # root hash for child class's superhash that maps state => action
18
+
19
+ class << self
20
+ def state_actions
21
+ @state_actions ||= SuperHash.new(superclass.state_actions)
22
+ end
23
+
24
+ def define_state_action(state, &action)
25
+ state_actions[state] = action
26
+ end
27
+ alias in_state define_state_action
28
+ end
29
+
30
+ define_state_action :no_op # all subclasses inherit this state
31
+
32
+ # instance methods
33
+
34
+ def state_actions
35
+ self.class.state_actions
36
+ end
37
+
38
+ def states
39
+ state_actions.keys
40
+ end
41
+
42
+ attr_reader :cur_state
43
+
44
+ def cur_state=(target_state)
45
+ unless states.include? target_state
46
+ raise "No way, dude. That's not a cool state for " +
47
+ "me to be in right now."
48
+ end
49
+ @cur_state = target_state
50
+ @cur_action = state_actions[target_state]
51
+ end
52
+
53
+ def run
54
+ @cur_action.call if @cur_action
55
+ end
56
+
57
+ def initialize(start_state = :no_op)
58
+ self.cur_state = start_state
59
+ end
60
+ end
61
+
62
+ class Computer < StateObject
63
+ in_state :off do
64
+ puts "Power off."
65
+ end
66
+
67
+ in_state :on do
68
+ puts "Power on."
69
+ end
70
+
71
+ in_state :boot do
72
+ puts "Can't boot -- no OS."
73
+ end
74
+ end
75
+
76
+ class WindowsNTBox < Computer
77
+ # Add a new state, and its action.
78
+ in_state :crashed do
79
+ puts "Blue screen!"
80
+ end
81
+
82
+ # Override an inherited state.
83
+ in_state :boot do
84
+ puts "Booting Windows NT."
85
+ end
86
+ end
87
+
88
+ pc = WindowsNTBox.new
89
+
90
+ seq = [:off, :on, :boot, :crashed, :off]
91
+
92
+ for state in seq
93
+ pc.cur_state = state
94
+ pc.run
95
+ end