rewritten 0.1.0 → 0.2.0

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