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/.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