rewritten 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.rdoc CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.2.0
2
+
3
+ * support for subdomains
4
+ * streamlined CRUD interface in web gui
5
+ * cleanup page
1
6
 
2
7
  == 0.1.0
3
8
 
@@ -9,4 +14,3 @@
9
14
  * fixed bug with unescaped keys in web gui
10
15
 
11
16
 
12
-
data/README.rdoc CHANGED
@@ -9,13 +9,14 @@ redirection or a modification of path and request parameters. For URLs
9
9
  without translation entries the request is left unmodified.
10
10
 
11
11
  Rewritten takes larges parts from the Resque codebase (which rocks). The
12
- gem is compromised of four parts:
12
+ gem is compromised of six parts:
13
13
 
14
14
  1. A Ruby library for creating, modifying and querying translations
15
15
  2. A Sinatra app for displaying and managing translations
16
- 3. A Rack app for rewriting and redirecting request (Rack::Rewritten::Url)
17
- 4. A Rack app for substituting URLs in HTML pages with their current translation (Rack::Rewritten::Html)
18
- 5. A Rack app for recording successful request (Rack::Rewritten::Record)
16
+ 3. A Rack app for identifying subdomains (Rack::Rewritten::Subdomain)
17
+ 4. A Rack app for rewriting and redirecting request (Rack::Rewritten::Url)
18
+ 5. A Rack app for substituting URLs in HTML pages with their current translation (Rack::Rewritten::Html)
19
+ 6. A Rack app for recording requests (Rack::Rewritten::Record)
19
20
 
20
21
 
21
22
  == New Relic Notice
@@ -43,9 +44,10 @@ Translations are removed in a similar fashion.
43
44
 
44
45
  == Usage in your Rack stack
45
46
 
46
- To take full advantage of the engine you would use both, the URL and
47
- HTML, rack apps as follows:
47
+ To take full advantage of the engine you would be using at least
48
+ the following stack:
48
49
 
50
+ use Rack::Rewritten::Subdomain, "example.com", "lvh.me" # only needed for subdomain support
49
51
  use Rack::Rewritten::Url
50
52
  use Rack::Rewritten::Html
51
53
  run Your::App
data/lib/rack/dummy.rb CHANGED
@@ -5,13 +5,16 @@ module Rack
5
5
  class Dummy
6
6
 
7
7
  def call(env)
8
- puts "-> Rack::Dummy"
9
- lines = []
10
- req = Rack::Request.new(env)
11
- lines << req.path
12
- lines << req.params.inspect
13
- lines << '<a href="/some/resource">'
14
- [200, {"Content-Type" => "text/plain"}, lines.join("\n")]
8
+ puts "-> Rack::Dummy"
9
+ lines = []
10
+ req = Rack::Request.new(env)
11
+ lines << req.path
12
+ lines << req.params.inspect
13
+ lines << req.host
14
+ lines << req.env.inspect
15
+ lines << "SUBDOMAIN: #{env['SUBDOMAIN']}"
16
+ lines << '<a href="/some/resource">'
17
+ [200, {"Content-Type" => "text/plain"}, lines.join("\n")]
15
18
  end
16
19
 
17
20
  end
@@ -0,0 +1,35 @@
1
+ require 'rack'
2
+
3
+ module Rack
4
+
5
+ module Rewritten
6
+
7
+ class Subdomain
8
+
9
+ def initialize(app, *fqdns)
10
+ @app = app
11
+ @fqdns = fqdns
12
+ end
13
+
14
+ def call(env)
15
+ puts "-> Rack::Rewritten::Subdomain"
16
+ req = Rack::Request.new(env)
17
+
18
+ @fqdns.each do |n|
19
+ if req.host =~ /(.+)\.#{n}$/
20
+ env["SUBDOMAIN"] = $1
21
+ env["FQDN"] = n
22
+ break
23
+ end
24
+ end
25
+
26
+ @app.call(env)
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+
data/lib/rack/url.rb CHANGED
@@ -14,8 +14,13 @@ module Rack
14
14
  puts "-> Rack::Rewritten::Url"
15
15
  req = Rack::Request.new(env)
16
16
 
17
- if to = ::Rewritten.redis.get("from:#{req.path_info}")
17
+ subdomain = env["SUBDOMAIN"] ? "#{env["SUBDOMAIN"]}:" : ""
18
+
19
+ puts "SUBDOMAIN: #{subdomain}"
20
+
21
+ if to = ::Rewritten.redis.get("from:#{subdomain}#{req.path_info}")
18
22
  current_path = ::Rewritten.list_range("to:#{to}", -1, 1)
23
+ current_path = current_path.split(":").last
19
24
  if current_path == req.path_info
20
25
  # if this is the current path, rewrite path and parameters
21
26
  tpath, tparams = split_to_path_params(to)
@@ -25,8 +30,15 @@ module Rack
25
30
  else
26
31
  # if this is not the current path, redirect to current path
27
32
  r = Rack::Response.new
28
- r.redirect(current_path, 301)
29
- r.finish
33
+ # NOTE: assuming redirection is always to non-subdomain-path
34
+ new_path = env["rack.url_scheme"]
35
+ new_path << "://"
36
+ new_path << env["HTTP_HOST"].sub(/^#{subdomain.chomp(':')}\./, '')
37
+ new_path << current_path
38
+ r.redirect(new_path, 301)
39
+ a = r.finish
40
+ puts a.inspect
41
+ a
30
42
  end
31
43
  else
32
44
  @app.call(req.env)
@@ -0,0 +1,28 @@
1
+
2
+ <h1>Cleanup</h1>
3
+
4
+ <% if @from_without_tos.size > 0 %>
5
+
6
+ <p>The following translations seem to be broken:</p>
7
+
8
+ <table class=''>
9
+ <tr>
10
+ <th>from URL (Browser Location)</th>
11
+ <th>to URL (Your App)</th>
12
+ </tr>
13
+
14
+ <% for from in @from_without_tos %>
15
+ <tr>
16
+ <td><%= from %></td>
17
+ <td></td>
18
+ <td><a href='<%= u("/delete?from=#{escape(from)}") %>'>delete</a></td>
19
+ </tr>
20
+ <% end %>
21
+
22
+ </table>
23
+
24
+ <% else %>
25
+ <p>No obviously broken translations found.</p>
26
+ <% end %>
27
+
28
+
@@ -1,5 +1,5 @@
1
1
  <div style="">
2
- <% if @from && @to %>
2
+ <% if @from %>
3
3
 
4
4
  <h2 style="width: 90%; overflow: scroll"><%= @from %> -> <%= @to %> </h2>
5
5
 
@@ -0,0 +1,40 @@
1
+
2
+ <div style="">
3
+ <% if @to %>
4
+
5
+ <h1 class='wi'><%= @to %> (App URL)</h1>
6
+
7
+ <table class='queues'>
8
+ <tr>
9
+ <th>Translations (Browser Location Bar) </th>
10
+ </tr>
11
+ <% for translation in Rewritten.get_all_translations(@to) %>
12
+ <tr>
13
+ <td class=''><a href="<%= translation %>" target="new"><%= translation %></td>
14
+ </tr>
15
+ <% end %>
16
+ </table>
17
+
18
+ <div style="float :left">
19
+
20
+ <p>
21
+ From a SEO perspective it's wise to keep the links and just
22
+ <a href='<%= u("/edit?to=#{escape(@to)}") %>'>add another translation</a> resulting in a 301 redirect.
23
+ </p>
24
+ <p>
25
+ Are you sure you want to delete all translations? THIS CANNOT BE UNDONE.
26
+ </p>
27
+
28
+ <form action='<%= u("/delete_all")%>' method="post" style="margin-top: 10px ">
29
+ <input type="hidden" name="_method" value="delete">
30
+ <input type="hidden" name="from" value="<%= @from %>">
31
+ <input type="hidden" name="to" value="<%= @to %>">
32
+ <input type="submit" value="Yes, I know; please do remove everything!">
33
+ <a href='<%= u("/to?to=#{escape(@to)}") %>'>Cancel</a>
34
+ </form>
35
+ </div>
36
+ <% else %>
37
+ <p>No translations found.</p>
38
+ <% end %>
39
+ </div>
40
+
@@ -0,0 +1,27 @@
1
+
2
+ <h1>Edit Translations</h1>
3
+
4
+ <p style="background: yellow; line-height:120%; padding: 10px; border: 1px solid black;"> Instead of removing (or renaming)
5
+ existing translations you often really want to add another entry to the
6
+ translation list. Because doing so will ensure that requests to old URLs
7
+ are redirected to their new location (a SEO friendly 301 redirect).
8
+ Especially older entries that may have already been used for links or in
9
+ indexes you will want to keep. </p>
10
+
11
+
12
+ <div style="float: left; margin-top: 10px">
13
+ <form method="POST" action="<%= u "/edit" %>" >
14
+
15
+ <%= partial :fields %>
16
+
17
+ <p>
18
+ <input type="submit" value="Change!" />
19
+ <a href='<%= u("/to?to=#{escape(@to)}") %>'>Cancel</a>
20
+ </p>
21
+
22
+ </form>
23
+ </div>
24
+
25
+
26
+
27
+
@@ -0,0 +1,18 @@
1
+
2
+ <input type="hidden" name="old" size="80" value="<%= @old_to %>"/> <br />
3
+
4
+ <label>
5
+ <h2>Translations (What's seen in the browser's location bar)</h2>
6
+ <p>Last line being the final location in the browser</p>
7
+ <p>Subdomains are to be prefixed with a colon, e.g. my.subdomain:/some/path</p>
8
+ </label><br/>
9
+
10
+ <textarea name="translations" cols="80" rows="10"><%= @translations.join("\n") %></textarea> <br />
11
+
12
+ <p>
13
+ <label><h2>Resource (What your app sees)</h2></label><br/>
14
+ <textarea name="to" cols="80" rows="2"><%= @to %></textarea>
15
+ </p>
16
+
17
+
18
+
@@ -1,17 +1,17 @@
1
1
 
2
-
3
- <h1>New Translation</h1>
4
-
2
+ <h1>+ Add Translations</h1>
5
3
 
6
4
  <div style="float: left; margin-top: 10px">
7
- <form method="POST" action="<%= u "/translations" %>" >
8
- <label>From (what the user sees) </label><br/> <input type="text" name="from" size="80"/> <br />
9
- <label>To (what your app sees) </label><br/> <textarea name="to" cols="80" rows="5" value="<%= params[:to] %>"></textarea> <br />
10
- <input type="submit" value="Create" />
11
- <a href='<%= u("/translations") %>'>Cancel</a>
12
- </form>
13
- </div>
5
+ <form method="POST" action="<%= u "/translations" %>" >
14
6
 
7
+ <%= partial :fields %>
15
8
 
9
+ <p>
10
+ <input type="submit" value="Add" />
11
+ <a href='<%= u("/") %>'>Cancel</a>
12
+ </p>
13
+
14
+ </form>
15
+ </div>
16
16
 
17
17
 
@@ -1,7 +1,15 @@
1
1
 
2
2
  <h1 class='wi'><%= params[:to] %> (App URL)</h1>
3
3
 
4
- <a href="<%= u(%|/new?to=#{Rack::Utils.escape(params[:to])}|) %>"> + add another translation </a>
4
+
5
+ <p>
6
+ <a href="<%= u(%|/edit?to=#{escape(params[:to])}|) %>">% rename</a>
7
+ </p>
8
+
9
+ <p>
10
+ <a href="<%= u(%|/delete_all?to=#{escape(params[:to])}|) %>">!! delete all</a>
11
+ </p>
12
+
5
13
 
6
14
  <table class='queues'>
7
15
  <tr>
@@ -12,7 +20,11 @@
12
20
  <% for translation in Rewritten.list_range("to:#{params[:to]}", 0, 100) %>
13
21
  <tr>
14
22
  <td class=''><a href="<%= translation %>" target="new"><%= translation %></td>
15
- <td class=''><a href="<%= u("/delete?from=#{translation}&to=#{escape(params[:to])}") %>">Delete</a></td>
23
+ <td class=''>
24
+ <a href="<%= u(%|/edit?to=#{escape(params[:to])}|) %>"> add </a>|
25
+ <a href="<%= u(%|/edit?to=#{escape(params[:to])}|) %>"> edit </a>|
26
+ <a href="<%= u(%|/edit?to=#{escape(params[:to])}|) %>"> remove </a>
27
+ </td>
16
28
  </tr>
17
29
  <% end %>
18
30
 
@@ -1,42 +1,42 @@
1
1
 
2
2
  <%# @subtabs = resque.queues unless partial? || params[:id].nil? %>
3
3
 
4
- <h1 class='wi'>Translations</h1>
5
-
6
- <p class='intro'>The list below contains all the registered translations from an
7
- url to target url. To view all translations for a target click on the
8
- target URL.</p>
9
-
10
- <a href="<%= u('/new') %>"> + New Translation </a>
11
-
12
-
13
- <form method="get">
14
- <label>Filter:</label> <input type="text" name="f" value="<%= params[:f] %>"/>
15
- <a href="<%= u('/translations') %>"> clear </a>
16
- </form>
17
-
18
- <p class='sub'>
19
- Showing <%= @start + 1%> to <%= [@size, @start + Rewritten.per_page].min %> of <b><%= @size %></b> translations
20
- <% if params[:f] && params[:f] != '' %>
21
- matching: <%= params[:f] %>.
22
- <% else %>.<% end %>
23
- </p>
24
-
25
- <table class=''>
4
+ <h1 class='wi'>Translations</h1>
5
+
6
+ <p class='intro'>The list below contains all the registered translations from an
7
+ url to target url. To view all translations for a target click on the
8
+ target URL.</p>
9
+
10
+ <p><a href="<%= u('/new') %>"> + New Translation </a></p>
11
+ <p><a href="<%= u('/cleanup') %>"> % Cleanup </a></p>
12
+
13
+ <form method="get">
14
+ <label>Filter:</label> <input type="text" name="f" value="<%= params[:f] %>"/>
15
+ <a href="<%= u('/translations') %>"> clear </a>
16
+ </form>
17
+
18
+ <p class='sub'>
19
+ Showing <%= @start + 1%> to <%= [@size, @start + Rewritten.per_page].min %> of <b><%= @size %></b> translations
20
+ <% if params[:f] && params[:f] != '' %>
21
+ matching: <%= params[:f] %>.
22
+ <% else %>.<% end %>
23
+ </p>
24
+
25
+ <table class=''>
26
+ <tr>
27
+ <th>from URL (Browser Location)</th>
28
+ <th>to URL (Your App)</th>
29
+ </tr>
30
+
31
+ <% for from,to in @translations %>
26
32
  <tr>
27
- <th>from URL (Browser Location)</th>
28
- <th>to URL (Your App)</th>
33
+ <td class=''><a href="<%= from %>" target="new"><%= from %></td>
34
+ <td class=''><a href="<%= u("/to?to=#{escape(to)}") %>"><%= to %></td>
29
35
  </tr>
30
-
31
- <% for from,to in @translations %>
32
- <tr>
33
- <td class=''><a href="<%= from %>" target="new"><%= from %></td>
34
- <td class=''><a href="<%= u("/to?to=#{escape(to)}") %>"><%= to %></td>
35
- </tr>
36
- <% end %>
36
+ <% end %>
37
37
 
38
38
  </table>
39
39
 
40
40
 
41
- <%= partial :next_more, :start => @start, :size => @size %>
41
+ <%= partial :next_more, :start => @start, :size => @size %>
42
42
 
@@ -78,6 +78,11 @@ module Rewritten
78
78
  end
79
79
  end
80
80
 
81
+ def extract_translations
82
+ text = params[:translations] or ""
83
+ text.split("\n").map(&:strip).reject(&:empty?)
84
+ end
85
+
81
86
  def show_args(args)
82
87
  Array(args).map { |a| a.inspect }.join("\n")
83
88
  end
@@ -173,12 +178,41 @@ module Rewritten
173
178
  end
174
179
 
175
180
  get "/new" do
181
+ @translations = []
176
182
  show "new"
177
183
  end
178
184
 
185
+ get "/edit" do
186
+ @old_to = @to = params[:to]
187
+ @translations = Rewritten.get_all_translations(@to)
188
+ show "edit"
189
+ end
190
+
191
+ post "/edit" do
192
+
193
+ @old_to = params[:old].strip
194
+ @to = params[:to].strip
195
+ @translations = extract_translations
196
+
197
+ if @to != '' and @translations.size > 0
198
+
199
+ # delete old translations
200
+ Rewritten.remove_all_translations(@old_to)
201
+ # create new translations
202
+ Rewritten.add_translations(@to, @translations)
203
+
204
+ redirect u("/to?to=#{escape(@to)}")
205
+ else
206
+ show "edit"
207
+ end
208
+
209
+ end
210
+
179
211
  post "/translations" do
180
- if params[:from]!='' && params[:to]!=''
181
- Rewritten.add_translation(params[:from], params[:to])
212
+ @to = params[:to].strip
213
+ @translations = extract_translations
214
+ if @to != '' && @translations.size > 0
215
+ Rewritten.add_translations(@to, @translations)
182
216
  redirect u('translations')
183
217
  else
184
218
  show "new"
@@ -190,6 +224,18 @@ module Rewritten
190
224
  show "to"
191
225
  end
192
226
 
227
+ get "/cleanup" do
228
+ # find keys that have no target
229
+ @from_without_tos= []
230
+ Rewritten.redis.lrange("froms", 0, -1).each do |from|
231
+ if Rewritten.redis.get("from:#{from}").empty?
232
+ @from_without_tos << from
233
+ end
234
+ end
235
+
236
+ show "cleanup"
237
+ end
238
+
193
239
  get "/delete" do
194
240
  @from = params[:from]
195
241
  @to = params[:to]
@@ -197,18 +243,20 @@ module Rewritten
197
243
  end
198
244
 
199
245
  post '/delete' do
200
-
201
246
  from = params[:from]
202
247
  to = params[:to]
203
-
204
248
  Rewritten.remove_translation(from, to)
249
+ redirect u("/")
250
+ end
205
251
 
206
- if Rewritten.num_translations(to) > 0
207
- redirect u("/to?to=#{escape(to)}")
208
- else
209
- redirect u("/")
210
- end
252
+ get "/delete_all" do
253
+ @to = params[:to]
254
+ show "delete_all"
255
+ end
211
256
 
257
+ post "/delete_all" do
258
+ Rewritten.remove_all_translations(params[:to])
259
+ redirect u("/")
212
260
  end
213
261
 
214
262
  get "/hits" do
@@ -225,10 +273,9 @@ module Rewritten
225
273
  end
226
274
 
227
275
  def self.tabs
228
- #@tabs ||= ["Overview", "Working", "Failed", "Queues", "Workers", "Stats"]
229
276
  @tabs ||= ["Translations", "Hits"]
230
277
  end
231
-
278
+
232
279
  end
233
280
  end
234
281
 
@@ -1,3 +1,3 @@
1
1
  module Rewritten
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/rewritten.rb CHANGED
@@ -5,6 +5,7 @@ require 'rack/dummy'
5
5
  require 'rack/url'
6
6
  require 'rack/record'
7
7
  require 'rack/html'
8
+ require 'rack/subdomain'
8
9
 
9
10
  module Rewritten
10
11
  include Helpers
@@ -131,6 +132,10 @@ module Rewritten
131
132
  redis.rpush("to:#{to}", from)
132
133
  end
133
134
 
135
+ def add_translations(to, froms)
136
+ froms.each {|from| add_translation(from, to)}
137
+ end
138
+
134
139
  def num_translations(to)
135
140
  Rewritten.size("to:#{to}")
136
141
  end
@@ -142,8 +147,18 @@ module Rewritten
142
147
  Rewritten.redis.lrem("tos", 0, to) if num_translations(to) == 0
143
148
  end
144
149
 
150
+ def remove_all_translations(to)
151
+ get_all_translations(to).each do |from|
152
+ Rewritten.remove_translation(from, to)
153
+ end
154
+ end
155
+
156
+ def get_all_translations(to)
157
+ Rewritten.redis.lrange("to:#{to}", 0, -1)
158
+ end
159
+
145
160
  def get_current_translation(path)
146
- translation = Rewritten.list_range("to:#{path}", -1, 1)
161
+ translation = Rewritten.list_range("to:#{path}", -1)
147
162
  return translation if translation
148
163
  return path
149
164
  end
data/lib/test.ru CHANGED
@@ -4,7 +4,8 @@ require 'rewritten'
4
4
  require 'rewritten/server'
5
5
 
6
6
  map "/" do
7
- use Rack::Rewritten::Record
7
+ #use Rack::Rewritten::Record
8
+ use Rack::Rewritten::Subdomain, "doxter.de", "lvh.me"
8
9
  use Rack::Rewritten::Url
9
10
  use Rack::Rewritten::Html
10
11
  run Rack::Dummy.new
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rewritten
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kai Rubarth
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-09-12 00:00:00 Z
18
+ date: 2011-09-23 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: redis-namespace
@@ -100,6 +100,7 @@ files:
100
100
  - lib/rack/dummy.rb
101
101
  - lib/rack/html.rb
102
102
  - lib/rack/record.rb
103
+ - lib/rack/subdomain.rb
103
104
  - lib/rack/url.rb
104
105
  - lib/rewritten.rb
105
106
  - lib/rewritten/config.ru
@@ -115,9 +116,13 @@ files:
115
116
  - lib/rewritten/server/public/style.css
116
117
  - lib/rewritten/server/public/working.png
117
118
  - lib/rewritten/server/test_helper.rb
119
+ - lib/rewritten/server/views/cleanup.erb
118
120
  - lib/rewritten/server/views/clear_hits.erb
119
121
  - lib/rewritten/server/views/delete.erb
122
+ - lib/rewritten/server/views/delete_all.erb
123
+ - lib/rewritten/server/views/edit.erb
120
124
  - lib/rewritten/server/views/error.erb
125
+ - lib/rewritten/server/views/fields.erb
121
126
  - lib/rewritten/server/views/hits.erb
122
127
  - lib/rewritten/server/views/layout.erb
123
128
  - lib/rewritten/server/views/new.erb