response_assembler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,23 @@
1
+ nbproject/*
2
+ .bundle/*
3
+ ## MAC OS
4
+ .DS_Store
5
+
6
+ ## TEXTMATE
7
+ *.tmproj
8
+ tmtags
9
+
10
+ ## EMACS
11
+ *~
12
+ \#*
13
+ .\#*
14
+
15
+ ## VIM
16
+ *.swp
17
+
18
+ ## PROJECT::GENERAL
19
+ coverage
20
+ rdoc
21
+ pkg
22
+
23
+ ## PROJECT::SPECIFIC
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ gem "rack", "1.2.1"
2
+ gem "rack-test", "0.5.5", :require => "rack/test"
3
+ gem "rspec", "2.0.0.beta.22"
4
+
5
+
@@ -0,0 +1,24 @@
1
+ GEM
2
+ specs:
3
+ diff-lcs (1.1.2)
4
+ rack (1.2.1)
5
+ rack-test (0.5.5)
6
+ rack (>= 1.0)
7
+ rspec (2.0.0.beta.22)
8
+ rspec-core (= 2.0.0.beta.22)
9
+ rspec-expectations (= 2.0.0.beta.22)
10
+ rspec-mocks (= 2.0.0.beta.22)
11
+ rspec-core (2.0.0.beta.22)
12
+ rspec-expectations (2.0.0.beta.22)
13
+ diff-lcs (>= 1.1.2)
14
+ rspec-mocks (2.0.0.beta.22)
15
+ rspec-core (= 2.0.0.beta.22)
16
+ rspec-expectations (= 2.0.0.beta.22)
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ rack (= 1.2.1)
23
+ rack-test (= 0.5.5)
24
+ rspec (= 2.0.0.beta.22)
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Hubert
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,57 @@
1
+ ResponseAssembler
2
+ =======================
3
+
4
+ ResponseAssembler is a piece of middleware that is meant to provide a way for quickly and easily assembling HTTP response documents from many "parts" (other responses). Let's say you want to have your pages built from posts, sidebar and menu - each of these can be returned by separate URLs (/posts/1, /sidebar, /menu) and ResponseAssembler will merge it for you into one HTML document.
5
+
6
+ ResponseAssembler can be used with Rack-Cache. For example, you might want to store your /posts/1 response in cache, and pull interactive (not cached) elements on the page. You can do it in Javascript on the client side, but to make it a bit nicer for google or lynx users (;)) you might want to assemble initial version on server side. We have provided you even a helper that mimicks XMLHttpRequest when you want to replace your current Javascript solution so you don't have to change your controllers! ResponseAssembler won't do caching for you, you need to use Rack-Cache instead and teach your app to use it.
7
+
8
+ You can use ResponseAssembler with CSS, JavaScript, text or CSV files. Please look at example to find out how to specify response mime types that ResponseAssembler should parse.
9
+
10
+ Installation
11
+ ============
12
+
13
+ Add this to Gemfile:
14
+
15
+ gem "response_assembler"
16
+
17
+ and run:
18
+
19
+ $ bundle install
20
+
21
+ Usage
22
+ =====
23
+
24
+ Say, you want to render single Post (/posts/1), and add extra menu, sidebar and comments boxes.
25
+ I assume you are using Rails.
26
+
27
+ To enable ResponseAssembler::Middleware, plact this line into your config/application.rb:
28
+
29
+ config.middleware.use "ResponseAssembler::Middleware"
30
+
31
+ Now, you don't have to change your PostsController::index action at all, just edit it's view template to return something like:
32
+
33
+ <get>/menu</get>
34
+ <get>/sidebar</get>
35
+ <h1><%= @post.title %></h1>
36
+ <p><%= @post.body %></p>
37
+ <get><%= post_comments_path @post %></get> // this is /posts/1/comments for example where your comments controller sits
38
+
39
+ You can see that your response will include what /menu /sidebar and /posts/1/comments returns.
40
+
41
+ Don't forget to add :layout => false to your /menu /sidebar and /posts/1/comments actions.
42
+
43
+ If you want to use the same logic for future AJAX requests (say for CommentsController), for example to skip rendering layout, you can mimick XMLHttpRequest by using <xhrget>/some/ajax/controller</xhrget> instead of normal "get".
44
+
45
+ Options
46
+ -------
47
+
48
+ In you config.ru file, you can use two extra options to initialize ResponseAssembler::Middleware.
49
+
50
+ ResponseAssembler will render <p>Part loading failed...</p> in places where responses from your app had different status code than 200 HTTP OK. If you want to change this message, just use:
51
+
52
+ use ResponseAssembler::Middleware, "Oops, can't find part"
53
+
54
+ and if you want to filter, say - only text/css files for inclusions, use array of content types at end of line:
55
+
56
+ use ResponseAssembler::Middleware, "/* Oops, can't find part of this CSS */", ["text/css"]
57
+
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "response_assembler"
8
+ gem.summary = %Q{ResponseAssembler assembles HTTP response documents from many "parts" (other responses)}
9
+ gem.description = %Q{Could be called parts for Rack...}
10
+ gem.email = "hubert.lepicki@amberbit.com"
11
+ gem.homepage = "http://github.com/hubertlepicki/response_assembler"
12
+ gem.authors = ["Hubert"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "responseassembler #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "response_assembler", "middleware")
@@ -0,0 +1,96 @@
1
+ #--
2
+ # Copyright (c) 2009 Hubert Lepicki <hubert.lepicki@gmail.com>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'rack'
25
+
26
+ # This class is piece of Rack middleware that filters responses from your
27
+ # app and finds <get>/some/relative/url</get> or <xhrget>/some/ajax/url</xhrget>
28
+ # and substitutes them with appropriate responses returned by your app.
29
+ # This can be useful if you want to assemble single page with responses returned
30
+ # by many controllers for example.
31
+ # xhrget performs standard GET request as well, and tries to mimick
32
+ # XMLHTTPRequest so you can use "if request.xhr?; render :layout => false; end"
33
+ # the same way you'd use it with JavaScript, to keep your controllers lean and
34
+ # clean.
35
+ module ResponseAssembler
36
+ class Middleware
37
+ # In Rails you could use:
38
+ # config.middleware.use "Rack::ResponseAssembler", "Yo! Can't load parts!", ["text/html"]
39
+ # to change error message and content types array that filter gets run on.
40
+ def initialize(app,
41
+ error_message = "<p>Loading failed...</p>",
42
+ content_types = ["text/html", "text/xhtml", "text/css", "text/csv", "text/plain"])
43
+ @app = app
44
+ @content_types = content_types
45
+ @error_message = error_message
46
+ end
47
+
48
+ def call(env)
49
+ @original_env = env.clone
50
+
51
+ status, headers, response = @app.call(env)
52
+ return [status, headers, response] unless is_allowed_content_type(headers["Content-Type"].to_s)
53
+
54
+ response = [assemble_from_parts(response)]
55
+ headers["Content-Length"] = response[0].size.to_s
56
+ [status, headers, response]
57
+ end
58
+
59
+ private
60
+
61
+ def assemble_from_parts(resp_body)
62
+ join_response(resp_body).gsub(/(<get>|<xhrget>)(.*?)(<\/get>|<\/xhrget>)/) do
63
+ assemble_from_parts(get($2, $1 != "<get>"))
64
+ end
65
+ end
66
+
67
+ def get(relative_url, ajax=false)
68
+ path_string, query_string = relative_url.split("?")
69
+
70
+ env = @original_env.merge({
71
+ "REQUEST_PATH" => path_string,
72
+ "PATH_INFO" => path_string,
73
+ "REQUEST_URI" => path_string,
74
+ "REQUEST_METHOD" => "GET",
75
+ "QUERY_STRING" => (query_string || '')
76
+ })
77
+
78
+ env = env.merge({"HTTP_X_REQUESTED_WITH"=>"XMLHttpRequest"}) if ajax
79
+
80
+ status, headers, response = @app.call(env)
81
+ return response if status == 200
82
+ [@error_message]
83
+ end
84
+
85
+ def is_allowed_content_type(content_type)
86
+ @content_types.each { |type| return true if content_type =~ Regexp.new(type) }
87
+ false
88
+ end
89
+
90
+ def join_response(resp)
91
+ joined = ""
92
+ resp.each { |element| joined += element }
93
+ joined
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,57 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{response_assembler}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Hubert"]
12
+ s.date = %q{2010-09-23}
13
+ s.description = %q{Could be called parts for Rack...}
14
+ s.email = %q{hubert.lepicki@amberbit.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.markdown"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE",
25
+ "README.markdown",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "lib/response_assembler.rb",
29
+ "lib/response_assembler/middleware.rb",
30
+ "response_assembler.gemspec",
31
+ "spec/response_assembler_spec.rb",
32
+ "spec/testapp.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/hubertlepicki/response_assembler}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.7}
38
+ s.summary = %q{ResponseAssembler assembles HTTP response documents from many "parts" (other responses)}
39
+ s.test_files = [
40
+ "spec/testapp.rb",
41
+ "spec/response_assembler_spec.rb"
42
+ ]
43
+
44
+ if s.respond_to? :specification_version then
45
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
50
+ else
51
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
52
+ end
53
+ else
54
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
55
+ end
56
+ end
57
+
@@ -0,0 +1,41 @@
1
+ require File.join(File.dirname(__FILE__), "..", "lib", "response_assembler", "middleware")
2
+ Bundler.require
3
+ require 'testapp'
4
+
5
+ describe "ResponseAssembler::Middleware" do
6
+ include Rack::Test::Methods
7
+
8
+ def app
9
+ ResponseAssembler::Middleware.new(TestApp.new, "Oh, no!")
10
+ end
11
+
12
+ it "pass response returned by application when no recognized tags were found" do
13
+ get '/'
14
+ last_response.should be_ok
15
+ last_response.body.should eql('Hello World')
16
+ end
17
+
18
+ it "renders response1 on /response1" do
19
+ get "/response1"
20
+ last_response.should be_ok
21
+ last_response.body.should == "GET_response1"
22
+ end
23
+
24
+ it "renders response2 on /response2" do
25
+ get "/response2"
26
+ last_response.should be_ok
27
+ last_response.body.should == "GET_response2"
28
+ end
29
+
30
+ it "should embed paths wrapped in <get> tags" do
31
+ get "/embed_two"
32
+ last_response.should be_ok
33
+ last_response.body.should == "[GET_response1] [GET_response2]"
34
+ end
35
+
36
+ it "should recognize ajax an non-ajax calls" do
37
+ get "/get_ajax_and_not_ajax"
38
+ last_response.should be_ok
39
+ last_response.body.should == "[Ajax!] [Non-Ajax!]"
40
+ end
41
+ end
@@ -0,0 +1,26 @@
1
+ class TestApp
2
+ def call(env)
3
+ case env["PATH_INFO"]
4
+ when "/" then
5
+ [200, { 'Content-Type' => 'text/html' }, ["Hello World"]]
6
+ when "/response1"
7
+ [200, { 'Content-Type' => 'text/html' }, ["GET_response1"]]
8
+ when "/response2"
9
+ [200, { 'Content-Type' => 'text/html' }, ["GET_response2"]]
10
+ when "/embed_one"
11
+ [200, { 'Content-Type' => 'text/html' }, ["<get>/embed_two</get>"]]
12
+ when "/embed_two"
13
+ [200, { 'Content-Type' => 'text/html' }, ["[<get>/response1</get>] [<get>/response2</get>]"]]
14
+ when "/ajax_or_not"
15
+ if env["HTTP_X_REQUESTED_WITH"]=="XMLHttpRequest"
16
+ [200, { 'Content-Type' => 'text/html' }, ["Ajax!"]]
17
+ else
18
+ [200, { 'Content-Type' => 'text/html' }, ["Non-Ajax!"]]
19
+ end
20
+ when "/get_ajax_and_not_ajax"
21
+ [200, { 'Content-Type' => 'text/html' }, ["[<xhrget>/ajax_or_not</xhrget>] [<get>/ajax_or_not</get>]"]]
22
+ else
23
+ [404, { 'Content-Type' => 'text/html' }, ["404 Not found"]]
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: response_assembler
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Hubert
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-23 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: thoughtbot-shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ description: Could be called parts for Rack...
34
+ email: hubert.lepicki@amberbit.com
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - LICENSE
41
+ - README.markdown
42
+ files:
43
+ - .document
44
+ - .gitignore
45
+ - Gemfile
46
+ - Gemfile.lock
47
+ - LICENSE
48
+ - README.markdown
49
+ - Rakefile
50
+ - VERSION
51
+ - lib/response_assembler.rb
52
+ - lib/response_assembler/middleware.rb
53
+ - response_assembler.gemspec
54
+ - spec/response_assembler_spec.rb
55
+ - spec/testapp.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/hubertlepicki/response_assembler
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.3.7
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: ResponseAssembler assembles HTTP response documents from many "parts" (other responses)
88
+ test_files:
89
+ - spec/testapp.rb
90
+ - spec/response_assembler_spec.rb