bang-bang 0.1.6

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 (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