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/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .idea
2
+ *.gem
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2-p290@bang-bang
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :gemcutter
2
+
3
+ gemspec :name => "bang-bang"
data/Gemfile.lock ADDED
@@ -0,0 +1,97 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bang-bang (0.1.5)
5
+ activesupport (>= 2.0.0)
6
+ honkster-addressable (>= 2.2.3)
7
+ mustache
8
+ named-routes (>= 0.2.7)
9
+ sinatra (>= 1.2.0)
10
+ yajl-ruby (>= 0.8.1)
11
+
12
+ GEM
13
+ remote: http://rubygems.org/
14
+ specs:
15
+ activesupport (3.0.9)
16
+ addressable (2.2.4)
17
+ archive-tar-minitar (0.5.2)
18
+ capybara (0.4.0)
19
+ celerity (>= 0.7.9)
20
+ culerity (>= 0.2.4)
21
+ mime-types (>= 1.16)
22
+ nokogiri (>= 1.3.3)
23
+ rack (>= 1.0.0)
24
+ rack-test (>= 0.5.4)
25
+ selenium-webdriver (>= 0.0.27)
26
+ xpath (~> 0.1.2)
27
+ celerity (0.8.8)
28
+ childprocess (0.1.7)
29
+ ffi (~> 0.6.3)
30
+ columnize (0.3.2)
31
+ crack (0.1.8)
32
+ culerity (0.2.15)
33
+ diff-lcs (1.1.2)
34
+ ffi (0.6.3)
35
+ rake (>= 0.8.7)
36
+ honkster-addressable (2.2.3)
37
+ i18n (0.6.0)
38
+ json_pure (1.5.1)
39
+ linecache19 (0.5.11)
40
+ ruby_core_source (>= 0.1.4)
41
+ mime-types (1.16)
42
+ mustache (0.99.4)
43
+ named-routes (0.2.7)
44
+ activesupport (>= 2.0.0)
45
+ nokogiri (1.4.4)
46
+ rack (1.2.2)
47
+ rack-test (0.5.6)
48
+ rack (>= 1.0)
49
+ rake (0.8.7)
50
+ rr (1.0.2)
51
+ rspec (2.2.0)
52
+ rspec-core (~> 2.2)
53
+ rspec-expectations (~> 2.2)
54
+ rspec-mocks (~> 2.2)
55
+ rspec-core (2.5.1)
56
+ rspec-expectations (2.5.0)
57
+ diff-lcs (~> 1.1.2)
58
+ rspec-mocks (2.5.0)
59
+ ruby-debug-base19 (0.11.24)
60
+ columnize (>= 0.3.1)
61
+ linecache19 (>= 0.5.11)
62
+ ruby_core_source (>= 0.1.4)
63
+ ruby-debug19 (0.11.6)
64
+ columnize (>= 0.3.1)
65
+ linecache19 (>= 0.5.11)
66
+ ruby-debug-base19 (>= 0.11.19)
67
+ ruby_core_source (0.1.4)
68
+ archive-tar-minitar (>= 0.5.2)
69
+ rubyzip (0.9.4)
70
+ selenium-webdriver (0.1.3)
71
+ childprocess (~> 0.1.5)
72
+ ffi (~> 0.6.3)
73
+ json_pure
74
+ rubyzip
75
+ sinatra (1.2.6)
76
+ rack (~> 1.1)
77
+ tilt (>= 1.2.2, < 2.0)
78
+ tilt (1.3.2)
79
+ webmock (1.6.2)
80
+ addressable (>= 2.2.2)
81
+ crack (>= 0.1.7)
82
+ xpath (0.1.3)
83
+ nokogiri (~> 1.3)
84
+ yajl-ruby (0.8.2)
85
+
86
+ PLATFORMS
87
+ ruby
88
+
89
+ DEPENDENCIES
90
+ bang-bang!
91
+ capybara (>= 0.4.0)
92
+ i18n (>= 0.5.0)
93
+ rack-test (>= 0.5.6)
94
+ rr (>= 1.0.2)
95
+ rspec (>= 2.2.0)
96
+ ruby-debug19 (>= 0.11.6)
97
+ webmock (>= 1.6.2)
data/README.markdown ADDED
@@ -0,0 +1,42 @@
1
+ # TrueWeb
2
+
3
+ TrueWeb is a lightweight Sinatra web VC stack. Currently, it doesn't have strong opinions, to allow flexibility to discover good idioms.
4
+
5
+ ## Basic Layout
6
+
7
+ TrueWeb has a basic app structure, similar to Rails.
8
+ Right now, no conventions are "forced" on you. This is done to allow experimentation and keep things lightweight.
9
+
10
+ You pick the model library.
11
+
12
+ Right now there are no generators.
13
+
14
+ ### TrueWeb::Controller
15
+
16
+ TrueWeb::Controller is a subclass of Sinatra::Base.
17
+ It's meant to provide an encapsulated unit of functionality within your app.
18
+ How you group your actions is up to you.
19
+
20
+ ### NamedRoutes
21
+
22
+ TrueWeb uses the [named-routes](https://github.com/btakita/named-routes) library.
23
+
24
+ Here is a good pattern to follow:
25
+
26
+ module MyApp
27
+ Controller.define_routes do
28
+ def root; end
29
+ get path(:root, "/") do
30
+ "Hello World!"
31
+ end
32
+
33
+ def about_us; end
34
+ get path(:about_us, "/about-us") do
35
+ view("/about-us.html.ms")
36
+ end
37
+ end
38
+ end
39
+
40
+ Defining the route methods allows certain code editors & IDEs to locate the definition of action.
41
+
42
+ uris.about_us # /about-us
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bang-bang.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+
5
+ require 'bang-bang/version'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "bang-bang"
9
+ s.version = ::BangBang::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = ["Brian Takita"]
12
+ s.email = ["btakita@truecar.com"]
13
+ s.homepage = "https://github.com/TrueCar/bang-bang"
14
+ s.summary = %q{A lightweight Sinatra web MVC stack}
15
+ s.description = %q{A lightweight Sinatra web MVC stack}
16
+
17
+ s.required_rubygems_version = ">= 1.3.6"
18
+
19
+ # Man files are required because they are ignored by git
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.require_paths = ["lib"]
23
+
24
+ s.add_dependency "activesupport", ">=2.0.0"
25
+ s.add_dependency "honkster-addressable", ">=2.2.3"
26
+ s.add_dependency "mustache"
27
+ s.add_dependency "named-routes", ">=0.2.7"
28
+ s.add_dependency "sinatra", ">=1.2.0"
29
+ s.add_dependency "yajl-ruby", ">=0.8.1"
30
+
31
+ s.add_development_dependency "capybara", ">=0.4.0"
32
+ s.add_development_dependency "i18n", ">=0.5.0"
33
+ s.add_development_dependency "rack-test", ">=0.5.6"
34
+ s.add_development_dependency "rr", ">=1.0.2"
35
+ s.add_development_dependency "rspec", ">=2.2.0"
36
+ s.add_development_dependency "ruby-debug19", ">=0.11.6"
37
+ s.add_development_dependency "webmock", ">=1.6.2"
38
+ end
@@ -0,0 +1,126 @@
1
+ module BangBang
2
+ class Controller < Sinatra::Base
3
+ set :raise_errors, false
4
+ set :show_exceptions, false
5
+
6
+ class_inheritable_accessor :config
7
+ extend(Module.new do
8
+ def uris
9
+ config.uris
10
+ end
11
+
12
+ def views_class
13
+ config.views_class
14
+ end
15
+
16
+ def path(*args, &block)
17
+ uris.path(*args, &block)
18
+ end
19
+
20
+ def services
21
+ config.services
22
+ end
23
+ end)
24
+
25
+ TEMPLATE_TYPE_NAME = "Template"
26
+
27
+ set :logging, true
28
+ set :static, true
29
+ attr_reader :helper, :views
30
+
31
+ error(403) do
32
+ "<div>Sorry, the page you're trying to access is private.</div>"
33
+ end
34
+
35
+ error(404) do
36
+ "<div>Sorry, we could not find the page you were looking for.</div>"
37
+ end
38
+
39
+ error(500) do
40
+ env["rack.exception"] = env["sinatra.error"]
41
+ if env["rack.logger"]
42
+ env["rack.logger"].error "#{env["sinatra.error"].message}\n#{env["sinatra.error"].backtrace.join("\n\t")}"
43
+ end
44
+ "<div>Oops, an error occurred.</div>"
45
+ end
46
+
47
+ before do
48
+ @views = self.class.views_class.new(self)
49
+ end
50
+
51
+ def services
52
+ self.class.services
53
+ end
54
+
55
+ def uris
56
+ self.class.uris
57
+ end
58
+
59
+ def view(path, *args)
60
+ views[path].render(*args)
61
+ end
62
+
63
+ def static!
64
+ nil
65
+ end
66
+
67
+ module Controllers
68
+ module ClassMethods
69
+ def define_routes(&route_definition)
70
+ controller = Class.new(self)
71
+ controller.class_eval(&route_definition)
72
+ controllers << controller
73
+ end
74
+
75
+ def controllers
76
+ @controllers ||= []
77
+ end
78
+
79
+ def pagelet_access_resources(pagelet_json_path, paglet_asset_json_path, html_path=nil)
80
+ if html_path
81
+ get html_path do
82
+ send(html_path)
83
+ end
84
+ end
85
+
86
+ get pagelet_json_path do
87
+ response['Content-Type'] = 'application/json'
88
+ send(paglet_asset_json_path).merge(
89
+ html_path ? {
90
+ :html => send(html_path)
91
+ } : {}
92
+ ).to_json
93
+ end
94
+
95
+ get paglet_asset_json_path do
96
+ response['Content-Type'] = 'application/json'
97
+ send(paglet_asset_json_path).to_json
98
+ end
99
+ end
100
+ end
101
+
102
+ def self.included(mod)
103
+ mod.extend(ClassMethods)
104
+ end
105
+
106
+ # Overrode call! to speed up routing to subresources.
107
+ def call!(env)
108
+ self.class.controllers.each do |controller|
109
+ request = ::Sinatra::Request.new(env)
110
+ routes = controller.routes[request.request_method] || []
111
+ path = unescape(request.path_info)
112
+ routes.each do |pattern, keys, conditions, block|
113
+ if pattern.match(path)
114
+ subapp_instance = controller.allocate
115
+ subapp_instance.send(:initialize, nil)
116
+ response = subapp_instance.call(env)
117
+ return response if response[0] != 404
118
+ end
119
+ end
120
+ end
121
+ super
122
+ end
123
+ end
124
+ include Controllers
125
+ end
126
+ end
@@ -0,0 +1,13 @@
1
+ module BangBang
2
+ module EnvMethods
3
+ def development?
4
+ rack_env == "development"
5
+ end
6
+
7
+ def rack_env
8
+ ENV["RACK_ENV"]
9
+ end
10
+
11
+ extend self
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module BangBang
2
+ module Plugins
3
+ module DirectoryFirstSort
4
+ def directory_first_sort
5
+ lambda do |path|
6
+ dir = File.dirname(path)
7
+ [dir.length, dir, File.basename(path)]
8
+ end
9
+ end
10
+
11
+ extend self
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ module BangBang
2
+ module Plugins
3
+ class Set
4
+ attr_reader :config
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def on_init(&block)
10
+ @on_init ||= []
11
+ if block
12
+ @on_init.push(block)
13
+ else
14
+ @on_init
15
+ end
16
+ end
17
+
18
+ def init
19
+ self.on_init.each do |block|
20
+ block.call(config)
21
+ end
22
+ end
23
+ end
24
+
25
+ Dir["#{File.dirname(__FILE__)}/plugins/*.rb"].each do |file|
26
+ require file
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,176 @@
1
+ module BangBang
2
+ class Service
3
+ attr_reader :root_dir
4
+ attr_accessor :url_prefix
5
+
6
+ def initialize(dir)
7
+ @root_dir = dir
8
+ end
9
+
10
+ def init
11
+ init_controllers
12
+ autoload_models
13
+ append_load_paths
14
+ eval_init_rb
15
+ yield(self) if block_given?
16
+ self
17
+ end
18
+
19
+ def append_load_paths
20
+ lib_dir = File.join(root_dir, "lib")
21
+ $LOAD_PATH << lib_dir if File.directory?(lib_dir)
22
+ end
23
+
24
+ def get_presenter_file_path(url)
25
+ stripped_url_prefix = strip_url_prefix(url)
26
+ file_path = File.join(presenters_dir, "#{stripped_url_prefix}.rb")
27
+ File.file?(file_path) ? file_path : nil
28
+ end
29
+
30
+ def get_stylesheet_file_path(url)
31
+ stripped_url_prefix = strip_url_prefix(url)
32
+ file_path = File.join(app_dir, stripped_url_prefix.gsub(/\.css$/, ".sass"))
33
+ File.file?(file_path) ? file_path : nil
34
+ end
35
+
36
+ def get_static_file_path(url)
37
+ stripped_url_prefix = strip_url_prefix(url)
38
+ static_file_dirs.each do |dir|
39
+ file_path = File.join(dir, stripped_url_prefix)
40
+ return file_path if File.file?(file_path)
41
+ end
42
+ nil
43
+ end
44
+
45
+ def get_spec_urls(url)
46
+ specs_url_prefix = File.join(url_prefix.to_s, "specs")
47
+ stripped_url_prefix = strip_url_prefix(url, specs_url_prefix)
48
+ path = File.join(spec_javascripts_dir, stripped_url_prefix.to_s)
49
+ get_specs_urls_from_path(path)
50
+ end
51
+
52
+ def get_specs_urls_from_path(path=spec_javascripts_dir)
53
+ (Dir["#{path}/**/*_spec.js"] + Dir["#{path}.js"]).flatten.compact.map do |file|
54
+ file.gsub(spec_javascripts_dir, File.join(url_prefix.to_s, "specs")).gsub("//", "/")
55
+ end
56
+ end
57
+
58
+ def get_spec_file_path(url)
59
+ specs_url_prefix = File.join(url_prefix.to_s, "specs")
60
+ stripped_url_prefix = strip_url_prefix(url, specs_url_prefix)
61
+ file_path = File.join(spec_javascripts_dir, stripped_url_prefix)
62
+ file_path if File.file?(file_path)
63
+ end
64
+
65
+ def app_dir
66
+ File.join(root_dir, "app")
67
+ end
68
+
69
+ def presenters_dir
70
+ File.join(app_dir, "presenters")
71
+ end
72
+
73
+ def templates_dir
74
+ File.join(app_dir, "templates")
75
+ end
76
+
77
+ def stylesheets_dir
78
+ File.join(app_dir, "stylesheets")
79
+ end
80
+
81
+ def templates_hash
82
+ Dir["#{templates_dir}/**/*.*"].inject({}) do |memo, path|
83
+ memo[path.gsub(templates_dir, url_prefix.to_s)] = File.read(path)
84
+ memo
85
+ end
86
+ end
87
+
88
+ def javascript_urls(params={})
89
+ return [] unless File.directory?(public_javascripts_dir)
90
+ glob = params[:glob] || "**/*.js"
91
+ cache_buster = params.has_key?(:cache_bust) ? params[:cache_bust] : true
92
+ Dir["#{public_javascripts_dir}/#{glob}"].sort_by(&BangBang::Plugins::DirectoryFirstSort.directory_first_sort).map do |file|
93
+ asset_url(file, file.gsub(public_javascripts_dir, "#{url_prefix}/javascripts"), cache_buster)
94
+ end
95
+ end
96
+
97
+ def stylesheet_urls(params = {})
98
+ return [] unless File.directory?(stylesheets_dir)
99
+ glob = params[:glob] || "**/[^_]*.css"
100
+ cache_buster = params.has_key?(:cache_bust) ? params[:cache_bust] : true
101
+ sass_glob = glob.gsub(/\.css$/, ".sass")
102
+ Dir["#{stylesheets_dir}/#{sass_glob}"].sort_by(&BangBang::Plugins::DirectoryFirstSort.directory_first_sort).map do |file|
103
+ asset_url(file, file.gsub(stylesheets_dir, "#{url_prefix}/stylesheets").gsub(/\.sass$/, ".css"), cache_buster)
104
+ end
105
+ end
106
+
107
+ def spec_javascripts_dir
108
+ File.join(root_dir, "spec/javascripts")
109
+ end
110
+
111
+ def init_controllers
112
+ controllers_dir = File.join(root_dir, "app/controllers")
113
+ if File.directory?(controllers_dir)
114
+ module_files = Dir["#{controllers_dir}/**/*.module.rb"]
115
+ module_files.each do |file|
116
+ require file
117
+ end
118
+
119
+ directory_first_sort = BangBang::Plugins::DirectoryFirstSort.directory_first_sort
120
+ (Dir["#{controllers_dir}/**/*.rb"] - module_files).sort_by(&directory_first_sort).each do |file|
121
+ require file
122
+ end
123
+ end
124
+ end
125
+
126
+ def autoload_models
127
+ models_dir = File.join(root_dir, "app/models")
128
+ if File.directory?(models_dir)
129
+ directory_first_sort = BangBang::Plugins::DirectoryFirstSort.directory_first_sort
130
+ Dir["#{models_dir}/**/*.rb"].sort_by(&directory_first_sort).each do |file|
131
+ const_name_parts = file.
132
+ gsub(models_dir, "").
133
+ gsub("-", "_").
134
+ gsub(/\.rb/, "").
135
+ camelize.
136
+ split("::").
137
+ select {|part| part.present?}
138
+ parent_const = const_name_parts[0..-2].join("::").constantize
139
+ parent_const.autoload const_name_parts[-1].to_sym, file
140
+ end
141
+ end
142
+ end
143
+
144
+ def public_dir
145
+ File.join(root_dir, "public")
146
+ end
147
+
148
+ protected
149
+ def asset_url(file, url_path, cache_buster)
150
+ if cache_buster
151
+ "#{url_path}?_cb=#{File.mtime(file).to_i}"
152
+ else
153
+ url_path
154
+ end
155
+ end
156
+
157
+ def static_file_dirs
158
+ [public_dir, templates_dir]
159
+ end
160
+
161
+ def public_javascripts_dir
162
+ File.join(public_dir, "javascripts")
163
+ end
164
+
165
+ def strip_url_prefix(url_path, prefix=url_prefix)
166
+ url_path.gsub(Regexp.new("^#{prefix}"), "")
167
+ end
168
+
169
+ def eval_init_rb
170
+ init_rb_path = "#{root_dir}/init.rb"
171
+ if File.exists?(init_rb_path)
172
+ instance_eval(File.read(init_rb_path), init_rb_path, 1)
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,3 @@
1
+ module BangBang
2
+ VERSION = "0.1.6"
3
+ end
@@ -0,0 +1,81 @@
1
+ module BangBang
2
+ class Views
3
+ delegate :cookies, :current_user, :logged_in_user, :request, :response, :uris, :env, :params, :services, :to => :app_instance
4
+
5
+ class << self
6
+ def mustache(template_path, &definition)
7
+ define_method_via_include(template_path, &definition)
8
+ define_method_via_include(template_path) do |*args|
9
+ Mustache.render(template_content(template_path), self.class.metadata(template_path).merge(super(*args)))
10
+ end
11
+ end
12
+
13
+ def text(path, &definition)
14
+ define_method_via_include(path, &definition)
15
+ end
16
+
17
+ def metadata(path)
18
+ {
19
+ 'data-template' => path,
20
+ 'data-type' => "Template"
21
+ }
22
+ end
23
+
24
+ def define_method_via_include(method_name, &definition)
25
+ include(Module.new do
26
+ define_method(method_name, &definition)
27
+ end)
28
+ end
29
+ end
30
+
31
+ attr_reader :app_instance
32
+
33
+ def initialize(app_instance)
34
+ @app_instance = app_instance
35
+ end
36
+
37
+ def app
38
+ app_instance.class
39
+ end
40
+
41
+ def config
42
+ app_instance.config
43
+ end
44
+
45
+ def [](path)
46
+ unless respond_to?(path)
47
+ config.services.find do |service|
48
+ presenter_path = service.get_presenter_file_path(path)
49
+ if presenter_path
50
+ class_eval File.read(presenter_path), presenter_path, 1
51
+ end
52
+ end || begin
53
+ self.class.mustache(path) {|*_| {}}
54
+ end
55
+ end
56
+
57
+ o = lambda do |*args|
58
+ send(path, *args)
59
+ end
60
+
61
+ def o.render(*args)
62
+ self.call(*args)
63
+ end
64
+ o
65
+ end
66
+
67
+ def template_content(relative_path)
68
+ File.read(full_path(relative_path))
69
+ end
70
+
71
+ protected
72
+
73
+ def full_path(relative_path)
74
+ services.each do |service|
75
+ path = service.get_static_file_path(relative_path)
76
+ return path if path
77
+ end
78
+ raise ArgumentError, "Path #{relative_path.inspect} does not exist in any of the services."
79
+ end
80
+ end
81
+ end