turning 0.0.1
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.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/lib/turning.rb +7 -0
- data/lib/turning/callbacks.rb +8 -0
- data/lib/turning/configuration.rb +9 -0
- data/lib/turning/file_storage.rb +32 -0
- data/lib/turning/listener.rb +25 -0
- data/lib/turning/listener_loader.rb +11 -0
- data/lib/turning/railtie.rb +25 -0
- data/lib/turning/renderer.rb +57 -0
- data/lib/turning/static_cascade.rb +18 -0
- data/lib/turning/version.rb +3 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/files.rb +21 -0
- data/spec/support/rails.rb +2 -0
- data/spec/support/views.rb +27 -0
- data/spec/testapp/config.ru +0 -0
- data/spec/testapp/config/application.rb +27 -0
- data/spec/testapp/config/routes.rb +3 -0
- data/spec/turning/callbacks_spec.rb +21 -0
- data/spec/turning/configuration_spec.rb +19 -0
- data/spec/turning/file_storage_spec.rb +32 -0
- data/spec/turning/integration_spec.rb +43 -0
- data/spec/turning/listener_loader_spec.rb +22 -0
- data/spec/turning/listener_spec.rb +64 -0
- data/spec/turning/renderer_spec.rb +71 -0
- data/spec/turning/static_cascade_spec.rb +31 -0
- data/turning.gemspec +26 -0
- metadata +226 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2012 Joe Ferris and Mike Burns
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Turning
|
|
2
|
+
|
|
3
|
+
A tiny framework to render views whenever data changes rather than re-rendering on every visit.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Create a listener:
|
|
8
|
+
|
|
9
|
+
# app/listeners/products_listener.rb
|
|
10
|
+
class ProductsListener < Turning::Listener
|
|
11
|
+
def listen
|
|
12
|
+
Product.on(:save) do |product|
|
|
13
|
+
render 'show', product_path(product), product: product
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Whenever a `Product` is saved, it will render the
|
|
19
|
+
`app/views/products/show.html.erb` view and save it in `public/static`. A Rack
|
|
20
|
+
middleware is included and autoconfigured to serve static pages from that
|
|
21
|
+
directory.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
Add this line to your application's Gemfile:
|
|
26
|
+
|
|
27
|
+
gem 'turning'
|
|
28
|
+
|
|
29
|
+
And then execute:
|
|
30
|
+
|
|
31
|
+
$ bundle
|
|
32
|
+
|
|
33
|
+
Or install it yourself as:
|
|
34
|
+
|
|
35
|
+
$ gem install turning
|
|
36
|
+
|
|
37
|
+
## Contributing
|
|
38
|
+
|
|
39
|
+
1. Fork it
|
|
40
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
41
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
|
42
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
43
|
+
5. Create new pull request
|
data/Rakefile
ADDED
data/lib/turning.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Turning
|
|
2
|
+
class FileStorage
|
|
3
|
+
def initialize(root_path)
|
|
4
|
+
@root_path = root_path
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def put(file_path, contents)
|
|
8
|
+
FileUtils.mkdir_p(parent_directory(file_path))
|
|
9
|
+
File.open(resolve(file_path), 'w') do |file|
|
|
10
|
+
file.write(contents)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(file_path)
|
|
15
|
+
begin
|
|
16
|
+
IO.read(resolve(file_path))
|
|
17
|
+
rescue Errno::ENOENT
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def resolve(file_path)
|
|
25
|
+
File.join(@root_path, "#{file_path}_contents")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def parent_directory(file_path)
|
|
29
|
+
File.dirname(resolve(file_path))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Turning
|
|
2
|
+
class Listener
|
|
3
|
+
def initialize(renderer = nil)
|
|
4
|
+
@renderer = renderer || default_renderer
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def render(template_name, path, assigns = {})
|
|
10
|
+
@renderer.render_to_file(template_name, path, assigns)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def default_renderer
|
|
14
|
+
Renderer.new(view_path, storage)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def view_path
|
|
18
|
+
self.class.name.underscore.sub(/_listener$/, '')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def storage
|
|
22
|
+
Rails.configuration.turning.storage
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Turning
|
|
2
|
+
class ListenerLoader
|
|
3
|
+
def self.load
|
|
4
|
+
Dir.glob(Rails.root.join('app', 'listeners', '**', '*.rb')).each do |listener_file|
|
|
5
|
+
Kernel.load(listener_file)
|
|
6
|
+
listener_class = File.basename(listener_file).gsub('.rb', '').classify.constantize
|
|
7
|
+
listener_class.new.listen
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'rails/railtie'
|
|
2
|
+
require 'turning/static_cascade'
|
|
3
|
+
require 'turning/file_storage'
|
|
4
|
+
require 'turning/configuration'
|
|
5
|
+
require 'turning/listener_loader'
|
|
6
|
+
|
|
7
|
+
module Turning
|
|
8
|
+
class Railtie < ::Rails::Railtie
|
|
9
|
+
config.turning = Configuration.new
|
|
10
|
+
|
|
11
|
+
config.to_prepare do
|
|
12
|
+
ListenerLoader.load
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
initializer('turning.middleware') do
|
|
16
|
+
config.app_middleware.use StaticCascade, config.turning.storage
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
initializer('turning.url_helpers') do
|
|
20
|
+
Turning::Listener.class_eval do
|
|
21
|
+
include Rails.application.routes.url_helpers
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'action_view'
|
|
2
|
+
require 'active_support/core_ext/module/attr_internal'
|
|
3
|
+
require 'active_support/log_subscriber'
|
|
4
|
+
require 'abstract_controller/view_paths'
|
|
5
|
+
require 'abstract_controller/rendering'
|
|
6
|
+
require 'abstract_controller/layouts'
|
|
7
|
+
require 'abstract_controller/helpers'
|
|
8
|
+
require 'action_controller/metal/helpers'
|
|
9
|
+
|
|
10
|
+
module Turning
|
|
11
|
+
class Renderer
|
|
12
|
+
def initialize(controller_path, storage)
|
|
13
|
+
@controller_path = controller_path
|
|
14
|
+
@storage = storage
|
|
15
|
+
@renderable = Class.new(AbstractController::Base) {
|
|
16
|
+
include AbstractController::Rendering
|
|
17
|
+
include AbstractController::Layouts
|
|
18
|
+
include ActionController::Helpers
|
|
19
|
+
include Rails.application.routes.url_helpers
|
|
20
|
+
attr_accessor :view_assigns
|
|
21
|
+
|
|
22
|
+
# Search for views based on the controller name
|
|
23
|
+
attr_accessor :controller_path
|
|
24
|
+
self.view_paths = ActionController::Base.view_paths
|
|
25
|
+
|
|
26
|
+
# Include all helpers from the application's helper paths
|
|
27
|
+
def self.helpers_path
|
|
28
|
+
Rails.application.helpers_paths
|
|
29
|
+
end
|
|
30
|
+
helper :all
|
|
31
|
+
|
|
32
|
+
# We can't protect against forgery in static HTML, because it requires a session
|
|
33
|
+
def protect_against_forgery?
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
helper_method :protect_against_forgery?
|
|
37
|
+
|
|
38
|
+
# Look for a "static" layout by default, but don't fail if it doesn't exist
|
|
39
|
+
def self.name
|
|
40
|
+
'StaticController'
|
|
41
|
+
end
|
|
42
|
+
layout nil
|
|
43
|
+
|
|
44
|
+
# So that controller.action_name continues to work
|
|
45
|
+
attr_accessor :action_name
|
|
46
|
+
}.new
|
|
47
|
+
@renderable.controller_path = @controller_path
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def render_to_file(template_name, path, assigns)
|
|
51
|
+
@renderable.view_assigns = assigns
|
|
52
|
+
@renderable.action_name = template_name
|
|
53
|
+
contents = @renderable.render_to_string(action: template_name)
|
|
54
|
+
@storage.put(path, contents)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Turning
|
|
2
|
+
class StaticCascade
|
|
3
|
+
def initialize(app, storage)
|
|
4
|
+
@app = app
|
|
5
|
+
@storage = storage
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def call(env)
|
|
9
|
+
if body = @storage.get(env['PATH_INFO'])
|
|
10
|
+
content_length = body.size
|
|
11
|
+
headers = { 'Content-Type' => 'text/html', 'Content-Length' => content_length.to_s }
|
|
12
|
+
[200, headers, [body]]
|
|
13
|
+
else
|
|
14
|
+
@app.call(env)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'rspec'
|
|
2
|
+
require 'turning'
|
|
3
|
+
require 'bourne'
|
|
4
|
+
require 'rack/test'
|
|
5
|
+
|
|
6
|
+
require './spec/testapp/config/application'
|
|
7
|
+
Dir["spec/support/**/*.rb"].each { |f| require File.expand_path(f) }
|
|
8
|
+
|
|
9
|
+
RSpec.configure do |config|
|
|
10
|
+
config.mock_with :mocha
|
|
11
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Files
|
|
2
|
+
def create_file(path, content)
|
|
3
|
+
full_path = Rails.root.join(path)
|
|
4
|
+
FileUtils.mkdir_p(File.dirname(full_path))
|
|
5
|
+
File.open(full_path, 'w') { |file| file.write(content) }
|
|
6
|
+
@files ||= []
|
|
7
|
+
@files << full_path
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def cleanup_files
|
|
11
|
+
(@files || []).each do |file|
|
|
12
|
+
FileUtils.rm_rf(file)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
RSpec.configure do |config|
|
|
18
|
+
config.include Files
|
|
19
|
+
|
|
20
|
+
config.after(:each) { cleanup_files }
|
|
21
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Views
|
|
2
|
+
def create_view(path, contents)
|
|
3
|
+
full_path = Rails.root.join('app', 'views', path)
|
|
4
|
+
FileUtils.mkdir_p(File.dirname(full_path))
|
|
5
|
+
File.open(full_path, 'w') { |file| file.write(contents) }
|
|
6
|
+
@view_files ||= []
|
|
7
|
+
@view_files << full_path
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def read_static_view(path)
|
|
11
|
+
IO.read(Rails.root.join('public', 'static', path))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def cleanup_views
|
|
15
|
+
(@view_files || []).each do |view_file|
|
|
16
|
+
FileUtils.rm_rf(view_file)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
FileUtils.rm_rf(Rails.root.join('public', 'static'))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
RSpec.configure do |config|
|
|
24
|
+
config.include Views
|
|
25
|
+
|
|
26
|
+
config.after(:each) { cleanup_views }
|
|
27
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'rails'
|
|
2
|
+
require 'action_controller/railtie'
|
|
3
|
+
|
|
4
|
+
module TestApp
|
|
5
|
+
APP_ROOT = File.expand_path('..', __FILE__).freeze
|
|
6
|
+
|
|
7
|
+
class Application < Rails::Application
|
|
8
|
+
config.encoding = "utf-8"
|
|
9
|
+
# config.paths.add 'config/routes', with: "#{APP_ROOT}/config/routes.rb"
|
|
10
|
+
# config.paths.add 'app/controllers', with: "#{APP_ROOT}/app/controllers"
|
|
11
|
+
# config.paths.add 'app/helpers', with: "#{APP_ROOT}/app/helpers"
|
|
12
|
+
# config.paths.add 'app/views', with: "#{APP_ROOT}/app/views"
|
|
13
|
+
# config.paths.add 'log', with: 'tmp/log'
|
|
14
|
+
config.cache_classes = true
|
|
15
|
+
config.whiny_nils = true
|
|
16
|
+
config.consider_all_requests_local = true
|
|
17
|
+
config.action_controller.perform_caching = false
|
|
18
|
+
config.action_dispatch.show_exceptions = false
|
|
19
|
+
config.action_controller.allow_forgery_protection = false
|
|
20
|
+
config.active_support.deprecation = :stderr
|
|
21
|
+
config.secret_token = "DIESEL" * 5 # so diesel
|
|
22
|
+
|
|
23
|
+
def require_environment!
|
|
24
|
+
initialize!
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'turning/callbacks'
|
|
3
|
+
|
|
4
|
+
describe Turning, 'callbacks' do
|
|
5
|
+
it 'runs a callback without mangling self' do
|
|
6
|
+
model_class = Class.new do
|
|
7
|
+
extend ActiveModel::Callbacks
|
|
8
|
+
define_model_callbacks :save
|
|
9
|
+
end
|
|
10
|
+
ran_with = nil
|
|
11
|
+
|
|
12
|
+
model_class.on(:save) do
|
|
13
|
+
ran_with = self
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
model = model_class.new
|
|
17
|
+
model.run_callbacks(:save)
|
|
18
|
+
|
|
19
|
+
ran_with.object_id.should == self.object_id
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'turning/configuration'
|
|
3
|
+
require 'turning/file_storage'
|
|
4
|
+
|
|
5
|
+
describe Turning::Configuration do
|
|
6
|
+
it 'has a default storage backend' do
|
|
7
|
+
storage = stub('storage')
|
|
8
|
+
Turning::FileStorage.stubs(new: storage)
|
|
9
|
+
configuration = Turning::Configuration.new
|
|
10
|
+
configuration.storage.should == storage
|
|
11
|
+
Turning::FileStorage.should have_received(:new).with(Rails.root.join('public/static'))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'can change the storage backend' do
|
|
15
|
+
configuration = Turning::Configuration.new
|
|
16
|
+
configuration.storage = :anything
|
|
17
|
+
configuration.storage.should == :anything
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'turning/file_storage'
|
|
3
|
+
|
|
4
|
+
describe Turning::FileStorage do
|
|
5
|
+
it 'returns the content of a file from the filesystem' do
|
|
6
|
+
root = Rails.root.join('public', 'static')
|
|
7
|
+
putter = Turning::FileStorage.new(root)
|
|
8
|
+
getter = Turning::FileStorage.new(root)
|
|
9
|
+
|
|
10
|
+
putter.put('examples/awesomeness.html', 'awesome!')
|
|
11
|
+
results = getter.get('examples/awesomeness.html')
|
|
12
|
+
|
|
13
|
+
results.should == 'awesome!'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'caches a file at the root' do
|
|
17
|
+
root = Rails.root.join('public', 'static')
|
|
18
|
+
storage = Turning::FileStorage.new(root)
|
|
19
|
+
|
|
20
|
+
storage.put('/', 'awesome!')
|
|
21
|
+
results = storage.get('/')
|
|
22
|
+
|
|
23
|
+
results.should == 'awesome!'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'returns nil for a file that does not exist' do
|
|
27
|
+
root = Rails.root.join('public', 'static')
|
|
28
|
+
getter = Turning::FileStorage.new(root)
|
|
29
|
+
|
|
30
|
+
getter.get('snakes').should be_nil
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'turning'
|
|
3
|
+
|
|
4
|
+
describe Turning do
|
|
5
|
+
include Rack::Test::Methods
|
|
6
|
+
|
|
7
|
+
it 'listens to events and renders the view to a static file' do
|
|
8
|
+
create_file('app/listeners/examples_listener.rb', <<-RUBY)
|
|
9
|
+
class ExamplesListener < Turning::Listener
|
|
10
|
+
def listen
|
|
11
|
+
Example.on_update do
|
|
12
|
+
render 'index', '/', greeting: 'Hello'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
RUBY
|
|
17
|
+
|
|
18
|
+
create_file('app/models/example.rb', <<-RUBY)
|
|
19
|
+
class Example
|
|
20
|
+
def self.on_update(&block)
|
|
21
|
+
@on_update = block
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.trigger_update
|
|
25
|
+
@on_update.call
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
RUBY
|
|
29
|
+
|
|
30
|
+
create_view('examples/index.html.erb', '<%= @greeting %>')
|
|
31
|
+
|
|
32
|
+
Turning::ListenerLoader.load
|
|
33
|
+
|
|
34
|
+
Example.trigger_update
|
|
35
|
+
|
|
36
|
+
get '/'
|
|
37
|
+
last_response.body.should == 'Hello'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def app
|
|
41
|
+
TestApp::Application
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'turning/listener_loader'
|
|
3
|
+
|
|
4
|
+
describe Turning::ListenerLoader do
|
|
5
|
+
it 'loads listeners' do
|
|
6
|
+
create_file('app/listeners/examples_listener.rb', <<-RUBY)
|
|
7
|
+
class ExamplesListener
|
|
8
|
+
def self.listening?
|
|
9
|
+
defined?(@@listening) && @@listening
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def listen
|
|
13
|
+
@@listening = true
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
RUBY
|
|
17
|
+
|
|
18
|
+
Turning::ListenerLoader.load
|
|
19
|
+
|
|
20
|
+
ExamplesListener.should be_listening
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'turning/listener'
|
|
3
|
+
|
|
4
|
+
describe Turning::Listener do
|
|
5
|
+
it 'sets up a listener' do
|
|
6
|
+
listening = false
|
|
7
|
+
concrete_listener = Class.new(Turning::Listener) do
|
|
8
|
+
define_method :listen do
|
|
9
|
+
listening = true
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
concrete_listener.new(mock_renderer).listen
|
|
14
|
+
|
|
15
|
+
listening.should be
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'renders' do
|
|
19
|
+
concrete_listener = Class.new(Turning::Listener) do
|
|
20
|
+
def do_it
|
|
21
|
+
render 'index', 'hello-index', greeting: 'hello'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
renderer = mock_renderer
|
|
25
|
+
|
|
26
|
+
concrete_listener.new(renderer).do_it
|
|
27
|
+
|
|
28
|
+
renderer.should have_rendered('index', 'hello-index', greeting: 'hello')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'renders without assigns' do
|
|
32
|
+
concrete_listener = Class.new(Turning::Listener) do
|
|
33
|
+
def do_it
|
|
34
|
+
render 'index', 'index-unassigned'
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
renderer = mock_renderer
|
|
38
|
+
|
|
39
|
+
concrete_listener.new(renderer).do_it
|
|
40
|
+
|
|
41
|
+
renderer.should have_rendered('index', 'index-unassigned', {})
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'provides url helpers' do
|
|
45
|
+
concrete_listener = Class.new(Turning::Listener) do
|
|
46
|
+
def do_it
|
|
47
|
+
render 'index', root_path
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
renderer = mock_renderer
|
|
51
|
+
|
|
52
|
+
concrete_listener.new(renderer).do_it
|
|
53
|
+
|
|
54
|
+
renderer.should have_rendered('index', '/', {})
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def mock_renderer
|
|
58
|
+
stub('mock renderer', render_to_file: nil)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def have_rendered(template_name, path, assigns)
|
|
62
|
+
have_received(:render_to_file).with(template_name, path, assigns)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'turning/renderer'
|
|
3
|
+
|
|
4
|
+
describe Turning::Renderer do
|
|
5
|
+
it 'renders to a file' do
|
|
6
|
+
create_view('examples/say_hello.html.erb', '<%= @greeting %>')
|
|
7
|
+
storage = mock_storage
|
|
8
|
+
renderer = Turning::Renderer.new('examples', storage)
|
|
9
|
+
renderer.render_to_file('say_hello', '/result', greeting: 'Hello')
|
|
10
|
+
|
|
11
|
+
storage.should have_static_view('/result', 'Hello')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'supplies built-in helpers' do
|
|
15
|
+
create_view('examples/go_home.html.erb', '<%= link_to "go home", root_path %>')
|
|
16
|
+
storage = mock_storage
|
|
17
|
+
renderer = Turning::Renderer.new('examples', storage)
|
|
18
|
+
renderer.render_to_file('go_home', '/result', {})
|
|
19
|
+
|
|
20
|
+
storage.should have_static_view('/result', %{<a href="/">go home</a>})
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'supplies custom helpers' do
|
|
24
|
+
create_file('app/helpers/greeting_helper.rb', <<-HELPER)
|
|
25
|
+
module GreetingHelper
|
|
26
|
+
def greet
|
|
27
|
+
'Hello'
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
HELPER
|
|
31
|
+
create_view('examples/greet_hello.html.erb', '<%= greet %>')
|
|
32
|
+
storage = mock_storage
|
|
33
|
+
renderer = Turning::Renderer.new('examples', storage)
|
|
34
|
+
renderer.render_to_file('greet_hello', '/examples/greet_hello', {})
|
|
35
|
+
|
|
36
|
+
storage.should have_static_view('/examples/greet_hello', 'Hello')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'disables forgery protection for static forms' do
|
|
40
|
+
create_view('examples/form.html.erb', "<%= form_tag '/' do %><% end %>")
|
|
41
|
+
renderer = Turning::Renderer.new('examples', mock_storage)
|
|
42
|
+
expect { renderer.render_to_file('form', '/form', {}) }.not_to raise_error
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'renders with a layout' do
|
|
46
|
+
create_view('examples/simple.html.erb', 'Hello')
|
|
47
|
+
create_view('layouts/static.html.erb', 'Check this: <%= yield %>')
|
|
48
|
+
storage = mock_storage
|
|
49
|
+
renderer = Turning::Renderer.new('examples', storage)
|
|
50
|
+
renderer.render_to_file('simple', '/examples/simple', {})
|
|
51
|
+
|
|
52
|
+
storage.should have_static_view('/examples/simple', 'Check this: Hello')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'provides an action name' do
|
|
56
|
+
create_view('examples/example_template.html.erb', '<%= controller.action_name %>')
|
|
57
|
+
storage = mock_storage
|
|
58
|
+
renderer = Turning::Renderer.new('examples', storage)
|
|
59
|
+
renderer.render_to_file('example_template', '/examples/example_template', {})
|
|
60
|
+
|
|
61
|
+
storage.should have_static_view('/examples/example_template', 'example_template')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def mock_storage
|
|
65
|
+
stub('storage', put: nil)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def have_static_view(path, contents)
|
|
69
|
+
have_received(:put).with(path, contents)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'turning/static_cascade'
|
|
3
|
+
|
|
4
|
+
describe Turning::StaticCascade do
|
|
5
|
+
it 'serves the file if it exists' do
|
|
6
|
+
app = stub('app')
|
|
7
|
+
fake_content_storage = stub('fake content storage', get: 'contents')
|
|
8
|
+
static_file_name = '/ponies'
|
|
9
|
+
static_cascade = Turning::StaticCascade.new(app, fake_content_storage)
|
|
10
|
+
|
|
11
|
+
response = static_cascade.call('PATH_INFO' => static_file_name)
|
|
12
|
+
fake_content_storage.should have_received(:get).with(static_file_name)
|
|
13
|
+
response.should == [
|
|
14
|
+
200,
|
|
15
|
+
{ 'Content-Type' => 'text/html', 'Content-Length' => 'contents'.size.to_s },
|
|
16
|
+
['contents']
|
|
17
|
+
]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'cascades if the file does not exist' do
|
|
21
|
+
app = stub('app', call: 'app response')
|
|
22
|
+
fake_content_storage = stub('fake content storage', get: nil)
|
|
23
|
+
env = { 'PATH_INFO' => '/ponies' }
|
|
24
|
+
static_cascade = Turning::StaticCascade.new(app, fake_content_storage)
|
|
25
|
+
|
|
26
|
+
response = static_cascade.call(env)
|
|
27
|
+
|
|
28
|
+
app.should have_received(:call).with(env)
|
|
29
|
+
response.should == 'app response'
|
|
30
|
+
end
|
|
31
|
+
end
|
data/turning.gemspec
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require File.expand_path('../lib/turning/version', __FILE__)
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |gem|
|
|
5
|
+
gem.authors = ["Joe Ferris"]
|
|
6
|
+
gem.email = ["jferris@thoughtbot.com"]
|
|
7
|
+
gem.description = %q{The wheels in the sky keep on turning}
|
|
8
|
+
gem.summary = %q{A tiny framework to render views whenever data changes rather than re-rendering on every visit.}
|
|
9
|
+
|
|
10
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
11
|
+
gem.files = `git ls-files`.split("\n")
|
|
12
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
13
|
+
gem.name = "turning"
|
|
14
|
+
gem.require_paths = ["lib"]
|
|
15
|
+
gem.version = Turning::VERSION
|
|
16
|
+
|
|
17
|
+
gem.add_dependency 'activemodel'
|
|
18
|
+
gem.add_dependency 'actionpack'
|
|
19
|
+
gem.add_dependency 'railties'
|
|
20
|
+
|
|
21
|
+
gem.add_development_dependency 'rspec'
|
|
22
|
+
gem.add_development_dependency 'rake'
|
|
23
|
+
gem.add_development_dependency 'bourne'
|
|
24
|
+
gem.add_development_dependency 'tzinfo'
|
|
25
|
+
gem.add_development_dependency 'rack-test'
|
|
26
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: turning
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Joe Ferris
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-08-03 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: activemodel
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ! '>='
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '0'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: actionpack
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
type: :runtime
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ! '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: railties
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
none: false
|
|
50
|
+
requirements:
|
|
51
|
+
- - ! '>='
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
type: :runtime
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ! '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
- !ruby/object:Gem::Dependency
|
|
63
|
+
name: rspec
|
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
|
65
|
+
none: false
|
|
66
|
+
requirements:
|
|
67
|
+
- - ! '>='
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
type: :development
|
|
71
|
+
prerelease: false
|
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
74
|
+
requirements:
|
|
75
|
+
- - ! '>='
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
- !ruby/object:Gem::Dependency
|
|
79
|
+
name: rake
|
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
|
81
|
+
none: false
|
|
82
|
+
requirements:
|
|
83
|
+
- - ! '>='
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: '0'
|
|
86
|
+
type: :development
|
|
87
|
+
prerelease: false
|
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
89
|
+
none: false
|
|
90
|
+
requirements:
|
|
91
|
+
- - ! '>='
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '0'
|
|
94
|
+
- !ruby/object:Gem::Dependency
|
|
95
|
+
name: bourne
|
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
|
97
|
+
none: false
|
|
98
|
+
requirements:
|
|
99
|
+
- - ! '>='
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '0'
|
|
102
|
+
type: :development
|
|
103
|
+
prerelease: false
|
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
105
|
+
none: false
|
|
106
|
+
requirements:
|
|
107
|
+
- - ! '>='
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: tzinfo
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
none: false
|
|
114
|
+
requirements:
|
|
115
|
+
- - ! '>='
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
none: false
|
|
122
|
+
requirements:
|
|
123
|
+
- - ! '>='
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '0'
|
|
126
|
+
- !ruby/object:Gem::Dependency
|
|
127
|
+
name: rack-test
|
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
|
129
|
+
none: false
|
|
130
|
+
requirements:
|
|
131
|
+
- - ! '>='
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: '0'
|
|
134
|
+
type: :development
|
|
135
|
+
prerelease: false
|
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
137
|
+
none: false
|
|
138
|
+
requirements:
|
|
139
|
+
- - ! '>='
|
|
140
|
+
- !ruby/object:Gem::Version
|
|
141
|
+
version: '0'
|
|
142
|
+
description: The wheels in the sky keep on turning
|
|
143
|
+
email:
|
|
144
|
+
- jferris@thoughtbot.com
|
|
145
|
+
executables: []
|
|
146
|
+
extensions: []
|
|
147
|
+
extra_rdoc_files: []
|
|
148
|
+
files:
|
|
149
|
+
- .gitignore
|
|
150
|
+
- Gemfile
|
|
151
|
+
- LICENSE
|
|
152
|
+
- README.md
|
|
153
|
+
- Rakefile
|
|
154
|
+
- lib/turning.rb
|
|
155
|
+
- lib/turning/callbacks.rb
|
|
156
|
+
- lib/turning/configuration.rb
|
|
157
|
+
- lib/turning/file_storage.rb
|
|
158
|
+
- lib/turning/listener.rb
|
|
159
|
+
- lib/turning/listener_loader.rb
|
|
160
|
+
- lib/turning/railtie.rb
|
|
161
|
+
- lib/turning/renderer.rb
|
|
162
|
+
- lib/turning/static_cascade.rb
|
|
163
|
+
- lib/turning/version.rb
|
|
164
|
+
- spec/spec_helper.rb
|
|
165
|
+
- spec/support/files.rb
|
|
166
|
+
- spec/support/rails.rb
|
|
167
|
+
- spec/support/views.rb
|
|
168
|
+
- spec/testapp/config.ru
|
|
169
|
+
- spec/testapp/config/application.rb
|
|
170
|
+
- spec/testapp/config/routes.rb
|
|
171
|
+
- spec/turning/callbacks_spec.rb
|
|
172
|
+
- spec/turning/configuration_spec.rb
|
|
173
|
+
- spec/turning/file_storage_spec.rb
|
|
174
|
+
- spec/turning/integration_spec.rb
|
|
175
|
+
- spec/turning/listener_loader_spec.rb
|
|
176
|
+
- spec/turning/listener_spec.rb
|
|
177
|
+
- spec/turning/renderer_spec.rb
|
|
178
|
+
- spec/turning/static_cascade_spec.rb
|
|
179
|
+
- turning.gemspec
|
|
180
|
+
homepage:
|
|
181
|
+
licenses: []
|
|
182
|
+
post_install_message:
|
|
183
|
+
rdoc_options: []
|
|
184
|
+
require_paths:
|
|
185
|
+
- lib
|
|
186
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
187
|
+
none: false
|
|
188
|
+
requirements:
|
|
189
|
+
- - ! '>='
|
|
190
|
+
- !ruby/object:Gem::Version
|
|
191
|
+
version: '0'
|
|
192
|
+
segments:
|
|
193
|
+
- 0
|
|
194
|
+
hash: 1693876209319691702
|
|
195
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
196
|
+
none: false
|
|
197
|
+
requirements:
|
|
198
|
+
- - ! '>='
|
|
199
|
+
- !ruby/object:Gem::Version
|
|
200
|
+
version: '0'
|
|
201
|
+
segments:
|
|
202
|
+
- 0
|
|
203
|
+
hash: 1693876209319691702
|
|
204
|
+
requirements: []
|
|
205
|
+
rubyforge_project:
|
|
206
|
+
rubygems_version: 1.8.21
|
|
207
|
+
signing_key:
|
|
208
|
+
specification_version: 3
|
|
209
|
+
summary: A tiny framework to render views whenever data changes rather than re-rendering
|
|
210
|
+
on every visit.
|
|
211
|
+
test_files:
|
|
212
|
+
- spec/spec_helper.rb
|
|
213
|
+
- spec/support/files.rb
|
|
214
|
+
- spec/support/rails.rb
|
|
215
|
+
- spec/support/views.rb
|
|
216
|
+
- spec/testapp/config.ru
|
|
217
|
+
- spec/testapp/config/application.rb
|
|
218
|
+
- spec/testapp/config/routes.rb
|
|
219
|
+
- spec/turning/callbacks_spec.rb
|
|
220
|
+
- spec/turning/configuration_spec.rb
|
|
221
|
+
- spec/turning/file_storage_spec.rb
|
|
222
|
+
- spec/turning/integration_spec.rb
|
|
223
|
+
- spec/turning/listener_loader_spec.rb
|
|
224
|
+
- spec/turning/listener_spec.rb
|
|
225
|
+
- spec/turning/renderer_spec.rb
|
|
226
|
+
- spec/turning/static_cascade_spec.rb
|