multigiri 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 +12 -0
- data/CHANGELOG +2 -0
- data/Gemfile +9 -0
- data/LICENSE +20 -0
- data/README.textile +21 -0
- data/TODO.txt +0 -0
- data/deps.rip +5 -0
- data/examples/config.ru +50 -0
- data/lib/multigiri/.gitignore +0 -0
- data/lib/multigiri/email_obfuscator.rb +20 -0
- data/lib/multigiri/google_analytics.rb +53 -0
- data/lib/multigiri/html5.rb +46 -0
- data/lib/multigiri/version.rb +7 -0
- data/lib/multigiri.rb +43 -0
- data/multigiri.gemspec +43 -0
- data/multigiri.pre.gemspec +8 -0
- data/spec/multigiri/.gitignore +0 -0
- data/spec/multigiri_spec.rb +10 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +7 -0
- data/tasks.rb +37 -0
- metadata +83 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Jakub Stastny aka Botanicus
|
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.
|
data/README.textile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
h1. About
|
2
|
+
|
3
|
+
h1. Usage
|
4
|
+
|
5
|
+
Take a look at an "example":http://github.com/botanicus/multigiri/blob/master/examples/config.ru. The point is:
|
6
|
+
|
7
|
+
<pre>
|
8
|
+
use Multigiri do
|
9
|
+
use HTML5::Forms
|
10
|
+
use HTML5::Hidden
|
11
|
+
use EmailObfuscator
|
12
|
+
use GoogleAnalytics, :my_tracking_code
|
13
|
+
end
|
14
|
+
</pre>
|
15
|
+
|
16
|
+
h1. Links
|
17
|
+
|
18
|
+
* "Source Code":http://github.com/botanicus/multigiri
|
19
|
+
* "Wiki":http://wiki.github.com/botanicus/multigiri
|
20
|
+
* "API Docs":http://rdoc.info/projects/botanicus/multigiri
|
21
|
+
* "Bug reporting":http://github.com/botanicus/multigiri/issues
|
data/TODO.txt
ADDED
File without changes
|
data/deps.rip
ADDED
data/examples/config.ru
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env rackup -p 4000 -s thin
|
2
|
+
|
3
|
+
$:.unshift(::File.expand_path("../../lib", __FILE__))
|
4
|
+
|
5
|
+
require "multigiri"
|
6
|
+
require "multigiri/html5"
|
7
|
+
require "multigiri/email_obfuscator"
|
8
|
+
require "multigiri/google_analytics"
|
9
|
+
|
10
|
+
# Multigiri change the last element of the rack array from body to
|
11
|
+
# nokogiri, document so instead of [status, headers, body] you'll get
|
12
|
+
# [status, headers, document]. After the multigiri block everything
|
13
|
+
# will get to normal, so middlewares whichrun later won't be affected.
|
14
|
+
use Multigiri do
|
15
|
+
use HTML5::Forms
|
16
|
+
use HTML5::Hidden
|
17
|
+
use EmailObfuscator
|
18
|
+
use GoogleAnalytics, :my_tracking_code
|
19
|
+
end
|
20
|
+
|
21
|
+
# It's especially important to run these middlewares after multigiri,
|
22
|
+
# because in multigiri middleware you probably change the resulted HTML
|
23
|
+
use ContentType
|
24
|
+
use ContentLength
|
25
|
+
|
26
|
+
# We can't use the trick with __END__ as is used in sinatra, since Rack
|
27
|
+
# authors are stupid idiots and the content of the rackup file is eval-ed.
|
28
|
+
# This crappy design leads to more problems than just this one.
|
29
|
+
# template = ::File.read(__FILE__).split("__END__").last
|
30
|
+
|
31
|
+
template = ::File.read(__FILE__).match(/=begin template\n(.+)\n=end/m)[1]
|
32
|
+
|
33
|
+
run lambda { |env| [200, Hash.new, [template]] }
|
34
|
+
|
35
|
+
=begin template
|
36
|
+
<html>
|
37
|
+
<body>
|
38
|
+
<h1>Hello World!</h1>
|
39
|
+
<a href='joe@example.com'>mail</a>
|
40
|
+
<p hidden>test</p>
|
41
|
+
<p hidden style="color:red">test</p>
|
42
|
+
<form method="GET">
|
43
|
+
</form>
|
44
|
+
<form method="PUT">
|
45
|
+
</form>
|
46
|
+
<form method="DELETE">
|
47
|
+
</form>
|
48
|
+
</body>
|
49
|
+
</html>
|
50
|
+
=end
|
File without changes
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class Multigiri
|
5
|
+
class EmailObfuscator
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
status, headers, document = @app.call(env)
|
12
|
+
# document.css("a[href~='@']").each do |element|
|
13
|
+
document.css("a[href]").each do |element|
|
14
|
+
element[:href] = element[:href].sub("@", "@")
|
15
|
+
end
|
16
|
+
[status, headers, document]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Add Google Analytics tracking to your page
|
4
|
+
|
5
|
+
# @example
|
6
|
+
# use Multigiri do
|
7
|
+
# use GoogleAnalytics, :my_tracking_code if Rango.production?
|
8
|
+
# end
|
9
|
+
|
10
|
+
module Rack
|
11
|
+
class Multigiri
|
12
|
+
class GoogleAnalytics
|
13
|
+
attr_accessor :tracking_code
|
14
|
+
def initialize(app, tracking_code)
|
15
|
+
@app, @tracking_code = app, tracking_code
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
status, headers, document = @app.call(env)
|
20
|
+
body = document.xpath("/html/body")[0]
|
21
|
+
|
22
|
+
# include scripts to the page
|
23
|
+
script(body, protocol_recognition_script)
|
24
|
+
script(body, tracker_script)
|
25
|
+
|
26
|
+
[status, headers, document]
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
def script(body, content)
|
31
|
+
script = Nokogiri::XML::Node.new(:script, body.document)
|
32
|
+
script.inner_html = content
|
33
|
+
body.add_child(script)
|
34
|
+
end
|
35
|
+
|
36
|
+
def protocol_recognition_script
|
37
|
+
<<-EOF
|
38
|
+
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
|
39
|
+
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
|
40
|
+
EOF
|
41
|
+
end
|
42
|
+
|
43
|
+
def tracker_script
|
44
|
+
<<-EOF
|
45
|
+
try {
|
46
|
+
var pageTracker = _gat._getTracker("#{self.tracking_code}");
|
47
|
+
pageTracker._trackPageview();
|
48
|
+
} catch(err) {}
|
49
|
+
EOF
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class Multigiri
|
5
|
+
module HTML5
|
6
|
+
class Forms
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
status, headers, document = @app.call(env)
|
13
|
+
nodes = document.css("form[method=PUT]") + document.css("form[method=DELETE]")
|
14
|
+
nodes.each do |form|
|
15
|
+
input = Nokogiri::XML::Node.new("input", document)
|
16
|
+
input[:type] = "hidden"
|
17
|
+
input[:name] = "_method"
|
18
|
+
input[:value] = form[:method]
|
19
|
+
form[:method] = "POST"
|
20
|
+
form.add_child(input)
|
21
|
+
end
|
22
|
+
[status, headers, document]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Hidden
|
27
|
+
def initialize(app)
|
28
|
+
@app = app
|
29
|
+
end
|
30
|
+
|
31
|
+
def call(env)
|
32
|
+
status, headers, document = @app.call(env)
|
33
|
+
document.css("[hidden]").each do |element|
|
34
|
+
element.remove_attribute("hidden")
|
35
|
+
if element[:style]
|
36
|
+
element[:style] += "; display: none"
|
37
|
+
else
|
38
|
+
element[:style] = "display:none"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
[status, headers, document]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/multigiri.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# use Multigiri do
|
4
|
+
# use GoogleAnalytics, :my_tracking_code
|
5
|
+
# use EmailObfuscator
|
6
|
+
# use HTML5::Forms
|
7
|
+
# use HTML5::Hidden
|
8
|
+
# end
|
9
|
+
|
10
|
+
require "nokogiri"
|
11
|
+
|
12
|
+
# TODO: args for nokogiri, export format, indentation etc
|
13
|
+
module Rack
|
14
|
+
class Multigiri
|
15
|
+
def initialize(app, &block)
|
16
|
+
@app, @block = app, block
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
# convert to a Nokogiri document
|
21
|
+
status, headers, chunks = @app.call(env)
|
22
|
+
# the only what we know about body is that it has to respond to #each
|
23
|
+
body = String.new
|
24
|
+
chunks.each { |chunk| body += chunk }
|
25
|
+
document = Nokogiri::HTML(body) # before
|
26
|
+
@stack = lambda { |env| [status, headers, document] }
|
27
|
+
|
28
|
+
# get middlewares
|
29
|
+
self.instance_eval(&@block) if @block
|
30
|
+
|
31
|
+
# convert back to a [String]
|
32
|
+
status, headers, document = @stack.call(env)
|
33
|
+
body = document.to_html(indentation: 2) # TODO
|
34
|
+
headers["Content-Length"] = body.length.to_s
|
35
|
+
[status, headers, [body]]
|
36
|
+
end
|
37
|
+
|
38
|
+
# GoogleAnalytics.new(Other.new(self), tracking_code)
|
39
|
+
def use(klass, *args, &block)
|
40
|
+
@stack = klass.new(@stack, *args, &block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/multigiri.gemspec
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env gem build
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
# Run ./multigiri.gemspec or gem build multigiri.gemspec
|
5
|
+
# NOTE: we can't use require_relative because when we run gem build, it use eval for executing this file
|
6
|
+
require File.join(File.dirname(__FILE__), "lib", "multigiri", "version")
|
7
|
+
require "base64"
|
8
|
+
|
9
|
+
Gem::Specification.new do |s|
|
10
|
+
s.name = "multigiri"
|
11
|
+
s.version = Rack::Multigiri::VERSION
|
12
|
+
s.authors = ["Jakub Stastny aka Botanicus"]
|
13
|
+
s.homepage = "http://github.com/botanicus/multigiri"
|
14
|
+
s.summary = "" # TODO: summary
|
15
|
+
s.description = "" # TODO: long description
|
16
|
+
s.cert_chain = nil
|
17
|
+
s.email = Base64.decode64("c3Rhc3RueUAxMDFpZGVhcy5jeg==\n")
|
18
|
+
s.has_rdoc = true
|
19
|
+
|
20
|
+
# files
|
21
|
+
s.files = `git ls-files`.split("\n")
|
22
|
+
|
23
|
+
s.executables = Dir["bin/*"].map(&File.method(:basename))
|
24
|
+
s.default_executable = "multigiri"
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
|
27
|
+
# Ruby version
|
28
|
+
s.required_ruby_version = ::Gem::Requirement.new(">= 1.9")
|
29
|
+
|
30
|
+
# runtime dependencies
|
31
|
+
s.add_dependency "nokogiri"
|
32
|
+
|
33
|
+
begin
|
34
|
+
require "changelog"
|
35
|
+
rescue LoadError
|
36
|
+
warn "You have to have changelog gem installed for post install message"
|
37
|
+
else
|
38
|
+
s.post_install_message = CHANGELOG.new.version_changes
|
39
|
+
end
|
40
|
+
|
41
|
+
# RubyForge
|
42
|
+
s.rubyforge_project = "multigiri"
|
43
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env gem build
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
# You might think this is a terrible mess and guess what, you're
|
5
|
+
# right mate! However say thanks to authors of RubyGems, not me.
|
6
|
+
eval(File.read("multigiri.gemspec")).tap do |specification|
|
7
|
+
specification.version = "#{specification.version}.pre"
|
8
|
+
end
|
File without changes
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
data/tasks.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!./script/nake
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
begin
|
5
|
+
require_relative "gems/environment.rb"
|
6
|
+
rescue LoadError
|
7
|
+
abort "You have to install bundler and run gem bundle first!"
|
8
|
+
end
|
9
|
+
|
10
|
+
ENV["PATH"] = "script:#{ENV["PATH"]}"
|
11
|
+
|
12
|
+
require "nake/tasks/gem"
|
13
|
+
require "nake/tasks/spec"
|
14
|
+
require "nake/tasks/release"
|
15
|
+
|
16
|
+
require_relative "lib/multigiri/version"
|
17
|
+
|
18
|
+
begin
|
19
|
+
load "code-cleaner.nake"
|
20
|
+
Nake::Task["hooks:whitespace:install"].tap do |task|
|
21
|
+
task.config[:path] = "script"
|
22
|
+
task.config[:encoding] = "utf-8"
|
23
|
+
task.config[:whitelist] = '(bin/[^/]+|.+\.(rb|rake|nake|thor|task))$'
|
24
|
+
end
|
25
|
+
rescue LoadError
|
26
|
+
warn "If you want to contribute to Multigiri, please install code-cleaner and then run ./tasks.rb hooks:whitespace:install to get Git pre-commit hook for removing trailing whitespace."
|
27
|
+
end
|
28
|
+
|
29
|
+
# Setup encoding, so all the operations
|
30
|
+
# with strings from another files will work
|
31
|
+
Encoding.default_internal = "utf-8"
|
32
|
+
Encoding.default_external = "utf-8"
|
33
|
+
|
34
|
+
Task[:build].config[:gemspec] = "multigiri.gemspec"
|
35
|
+
Task[:prerelease].config[:gemspec] = "multigiri.pre.gemspec"
|
36
|
+
Task[:release].config[:name] = "multigiri"
|
37
|
+
Task[:release].config[:version] = Multigiri::VERSION
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: multigiri
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jakub Stastny aka Botanicus
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
date: 2010-02-03 00:00:00 +00:00
|
12
|
+
default_executable: multigiri
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nokogiri
|
16
|
+
type: :runtime
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
description: ""
|
25
|
+
email: stastny@101ideas.cz
|
26
|
+
executables: []
|
27
|
+
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files: []
|
31
|
+
|
32
|
+
files:
|
33
|
+
- .gitignore
|
34
|
+
- CHANGELOG
|
35
|
+
- Gemfile
|
36
|
+
- LICENSE
|
37
|
+
- README.textile
|
38
|
+
- TODO.txt
|
39
|
+
- deps.rip
|
40
|
+
- examples/config.ru
|
41
|
+
- lib/multigiri.rb
|
42
|
+
- lib/multigiri/.gitignore
|
43
|
+
- lib/multigiri/email_obfuscator.rb
|
44
|
+
- lib/multigiri/google_analytics.rb
|
45
|
+
- lib/multigiri/html5.rb
|
46
|
+
- lib/multigiri/version.rb
|
47
|
+
- multigiri.gemspec
|
48
|
+
- multigiri.pre.gemspec
|
49
|
+
- spec/multigiri/.gitignore
|
50
|
+
- spec/multigiri_spec.rb
|
51
|
+
- spec/spec.opts
|
52
|
+
- spec/spec_helper.rb
|
53
|
+
- tasks.rb
|
54
|
+
has_rdoc: true
|
55
|
+
homepage: http://github.com/botanicus/multigiri
|
56
|
+
licenses: []
|
57
|
+
|
58
|
+
post_install_message: "[\e[32mVersion 0.0.1\e[0m] Initial import\n"
|
59
|
+
rdoc_options: []
|
60
|
+
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "1.9"
|
68
|
+
version:
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project: multigiri
|
78
|
+
rubygems_version: 1.3.5
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: ""
|
82
|
+
test_files: []
|
83
|
+
|