short 0.4.3 → 0.5.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/README.md CHANGED
@@ -9,13 +9,20 @@ for obvious reasons, the demo is configured with S3 disabled. However, if you ar
9
9
  to play around with `short` follow the instructions below and use `http://shortener1.heroku.com`
10
10
  as your the url you want to use.
11
11
 
12
+ ## Upgrading to 0.5.0
13
+
14
+ v0.5.0 updates how shortener stores some data. To assist in keeping your data,
15
+ v0.5.0 also provides a `rake short:data:dehyphenate_keys` task that will update
16
+ your existing data to the new schema. If have data from < 0.5 that you plan on
17
+ using in a >= 0.5 world, you should run this task.
18
+
12
19
  ### Installation
13
20
 
14
- is now as easy as
21
+ is now as easy as
15
22
 
16
23
  `gem install short`
17
24
 
18
- and
25
+ and
19
26
 
20
27
  `short`
21
28
 
@@ -84,7 +91,7 @@ the `Shortener` class or directly through the `Shortener::Short` class. You get:
84
91
  * fetch
85
92
  * delete
86
93
 
87
- methods, each of which will return a/n istance of the `Short` class which will
94
+ methods, each of which will return a/n istance of the `Short` class which will
88
95
  parse the data and provide some defaults and access to said data.
89
96
 
90
97
  ### Executable
data/bin/short CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
2
+ $: << File.expand_path(File.dirname(__FILE__) + '/../lib')
3
3
 
4
4
  require 'shortener'
5
5
 
@@ -89,11 +89,11 @@ shorts: #{index.length}
89
89
  EOD
90
90
  end
91
91
 
92
- def rake(arg)
93
- puts "running shortener rake task: #{arg}"
92
+ def build
94
93
  gem_dir = File.expand_path(File.dirname(File.dirname(__FILE__)))
95
- cmd = "cd #{gem_dir} && rake #{arg}"
96
- puts `#{cmd}`
94
+ require 'rake'; load 'shortener/tasks/heroku.rake'
95
+ Rake::Task[:'short:heroku:build'].execute
96
+ puts "Your shortener repo has been created, cd there to finish up the process"
97
97
  end
98
98
 
99
99
  def do_action(act, arg)
@@ -114,7 +114,7 @@ def usage
114
114
  delete: delete a short from the index.
115
115
  index: show summary data for all shorts.
116
116
 
117
- rake: run a shortener rake task. [heroku:build, heroku:setup...]
117
+ build: Generate a shortener server folder.
118
118
  server: start an instance of the shortener server locally.
119
119
 
120
120
  the default command is shorten, so that one could
@@ -167,8 +167,9 @@ when 'server'
167
167
  start_web
168
168
  when 'index'
169
169
  show_index
170
- when 'rake'
171
- do_action(:rake, ARGV[1])
170
+ when 'build'
171
+ #do_action(:build, ARGV[1])
172
+ build
172
173
  when 'delete'
173
174
  do_action(:delete, ARGV[1])
174
175
  when '-v', '--version'
@@ -1,5 +1,6 @@
1
1
  require 'uri'
2
2
  require 'yaml'
3
+ require 'redis-namespace'
3
4
 
4
5
  class Shortener
5
6
  # The class for storing Configuration Information
@@ -15,7 +16,7 @@ class Shortener
15
16
 
16
17
  OPTIONS = [:SHORTENER_URL, :DEFAULT_URL, :REDISTOGO_URL, :S3_KEY_PREFIX,
17
18
  :S3_ACCESS_KEY_ID, :S3_SECRET_ACCESS_KEY, :S3_DEFAULT_ACL, :S3_BUCKET,
18
- :DOTFILE_PATH, :S3_ENABLED]
19
+ :DOTFILE_PATH, :S3_ENABLED, :SHORTENER_NS]
19
20
 
20
21
  HEROKU_IGNORE = [:DOTFILE_PATH, :SHORTENER_URL, :REDISTOGO_URL]
21
22
 
@@ -32,6 +33,7 @@ class Shortener
32
33
  check_env
33
34
  @options = @options.merge!(opts)
34
35
  @options[:DEFAULT_URL] ||= '/index'
36
+ @options[:SHORTENER_NS] ||= :shortener
35
37
  else
36
38
  @options = Configuration.current.options
37
39
  end
@@ -61,6 +63,14 @@ class Shortener
61
63
  @options[opt]
62
64
  end
63
65
  end
66
+ alias ns shortener_ns
67
+
68
+ # a configured redis namespace instance
69
+ def redis
70
+ uri = self.redistogo_url
71
+ _r = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
72
+ Redis::Namespace.new(self.ns, redis: _r)
73
+ end
64
74
 
65
75
  # return the URI for the redistogo url
66
76
  def redistogo_url
@@ -82,7 +92,7 @@ class Shortener
82
92
  # are the necessary options present for S3 to work?
83
93
  def s3_configured
84
94
  ret = true
85
- [:S3_KEY_PREFIX, :S3_ACCESS_KEY_ID, :S3_SECRET_ACCESS_KEY,
95
+ [:S3_KEY_PREFIX, :S3_ACCESS_KEY_ID, :S3_SECRET_ACCESS_KEY,
86
96
  :S3_DEFAULT_ACL, :S3_BUCKET ].each do |k|
87
97
  ret = !@options[k].nil? unless ret == false
88
98
  end
@@ -14,8 +14,8 @@
14
14
  %td= short['shortened']
15
15
  %td
16
16
  %a{href: short['url']}= short['url'][0..50] + ((short['url'].length > 50) ? '...' : '')
17
- %td= short['set-count']
18
- %td= short['click-count'] || 0
17
+ %td= short['set_count']
18
+ %td= short['click_count'] || 0
19
19
  %td= ttl_display(short['expire'])
20
20
  %td= short['max-clicks'] || "&infin;"
21
21
  %td
@@ -92,7 +92,7 @@ class Shortener
92
92
  url = "https://s3.amazonaws.com/#{$conf.s3_bucket}/#{$conf.s3_key_prefix}/#{fname}"
93
93
  ext = File.extname(fname)[1..-1]
94
94
  data = {'url' => url, 's3' => true, 'shortened' => key,
95
- 'extension' => ext, 'set-count' => 1}
95
+ 'extension' => ext, 'set_count' => 1}
96
96
  data = params.merge(data)
97
97
 
98
98
  $redis.set(key, sha)
@@ -117,7 +117,7 @@ class Shortener
117
117
  unless options['expire'] || options['max-clicks']
118
118
  if (!prev_set['max-clicks'] && !prev_set['expire'] &&
119
119
  (prev_set['url'] == url.to_s))
120
- $redis.hincrby(check_key, 'set-count', 1)
120
+ $redis.hincrby(check_key, 'set_count', 1)
121
121
  return prev_set
122
122
  end
123
123
  end
@@ -138,7 +138,7 @@ class Shortener
138
138
  sha = Digest::SHA1.hexdigest(url.to_s)
139
139
  $redis.set(key, sha)
140
140
 
141
- hsh_data = {'shortened' => key, 'url' => url, 'set-count' => 1}
141
+ hsh_data = {'shortened' => key, 'url' => url, 'set_count' => 1}
142
142
  hsh_data['max-clicks'] = options['max-clicks'].to_i if options['max-clicks']
143
143
 
144
144
  if options['expire'] # set expire time if specified
@@ -159,7 +159,7 @@ class Shortener
159
159
  $redis.keys("data:#{sha}:*").each do |key|
160
160
  short = $redis.hgetall(key)
161
161
  unless short == {} || short['expire'] || short['max-clicks']
162
- $redis.hincrby(key, 'set-count', 1)
162
+ $redis.hincrby(key, 'set_count', 1)
163
163
  return short
164
164
  end
165
165
  end
@@ -277,14 +277,14 @@ class Shortener
277
277
  key = "data:#{sha}:#{id}"
278
278
  short = $redis.hgetall(key)
279
279
  not_expired = short.has_key?('expire') ? $redis.get(short['expire']) : true
280
- not_maxed = !(short['click-count'].to_i >= short['max-clicks'].to_i)
280
+ not_maxed = !(short['click_count'].to_i >= short['max-clicks'].to_i)
281
281
  short.has_key?('max-clicks') ? not_maxed : not_maxed = true
282
282
  if params[:captures].last == '.json'
283
283
  ret = short.merge({expired: not_expired.nil? , maxed: !not_maxed})
284
284
  content_type :json
285
285
  return ret.to_json
286
286
  else
287
- $redis.hincrby(key, 'click-count', 1) if not_expired && not_maxed
287
+ $redis.hincrby(key, 'click_count', 1) if not_expired && not_maxed
288
288
  if not_expired
289
289
  unless short['s3'] == 'true' && !(short['type'] == 'download')
290
290
  if not_maxed
@@ -0,0 +1,178 @@
1
+
2
+ namespace :short do
3
+
4
+ namespace :heroku do
5
+ $gem_dir = File.expand_path(File.dirname(File.dirname(__FILE__)))
6
+
7
+ def gem_file(f)
8
+ #ret = args.map {|f| File.join($gem_dir, f)}.join(" ")
9
+ File.join($gem_dir, f.to_s)
10
+ end
11
+
12
+ def _file(f)
13
+ fs = $existing_repo ? [f.to_s] : ['heroku', f.to_s]
14
+ File.join(Dir.pwd, *fs)
15
+ end
16
+
17
+ def _ep(f)
18
+ beginning_int = case f.split('/')[-3]
19
+ when 'shortener'
20
+ puts "************short" if ENV['VVERBOSE']
21
+ -2
22
+ when 'public', 'views', 's3'
23
+ puts "************public || s3" if ENV['VVERBOSE']
24
+ -4
25
+ when 'skin'
26
+ puts "************views" if ENV['VVERBOSE']
27
+ -5
28
+ else
29
+ puts "************else #{f.split('/')[-3]}" if ENV['VVERBOSE']
30
+ -3
31
+ end
32
+ end_point = f.split('/')[beginning_int..-1].join('/')
33
+ end
34
+
35
+ def _l(action, start, nd = nil)
36
+ if ENV['VERBOSE']
37
+ msg = "#{action}: #{start} "
38
+ msg += "=> #{nd}" unless nd.nil?
39
+ puts msg
40
+ end
41
+ end
42
+
43
+ def recursively_remove_files(dir)
44
+ dirs = Array.new
45
+ Dir[File.join(dir, '*')].each do |f|
46
+ next if f =~ /^(\.|\..*)/
47
+ if File.directory?(f)
48
+ if Dir.entries(f).empty?
49
+ _l(:removing_dir, f)
50
+ FileUtils.rmdir(f)
51
+ else
52
+ dirs << f
53
+ recursively_remove_files(f)
54
+ end
55
+ else
56
+ _l(:removing, f)
57
+ FileUtils.rm(f)
58
+ end
59
+ end
60
+ _l(:removing_dir, dirs)
61
+ FileUtils.rmdir(dirs)
62
+ end
63
+
64
+ desc "Build a Heroku Ready Git repo"
65
+ task :build do
66
+ FileUtils.mkdir(File.join(Dir.pwd, 'heroku')) unless $existing_repo
67
+ [:'server', :'server/public', :'server/views',
68
+ :'server/views/s3', :'server/public/flash',
69
+ :'server/public/skin', :'server/public/images',
70
+ :'server/public/skin/blue.monday'].each do |f|
71
+ unless File.exist?(_file(f))
72
+ puts "creating #{_file(f)}" if ENV['VERBOSE']
73
+ FileUtils.mkdir(_file(f))
74
+ end
75
+ end
76
+
77
+ ['server', 'server/public', 'server/views', :'server/views/s3',
78
+ :'server/public/flash', :'server/public/images', :'server/public/skin',
79
+ :'server/public/skin/blue.monday'].each do |end_point|
80
+ Dir["#{$gem_dir}/#{end_point}/**"].each do |f|
81
+ next if File.directory?(f)
82
+ end_point = _file(:"#{_ep(f)}")
83
+ _l(:copying, f, end_point)
84
+ FileUtils.cp(f, end_point)
85
+ end
86
+ end
87
+ _s, _e = gem_file('server.rb'), _file(:'main.rb')
88
+ _l(:copying, _s, _e)
89
+ FileUtils.cp(_s, _e)
90
+ _s, _e = gem_file('configuration.rb'), _file(:'configuration.rb')
91
+ _l(:copying, _s, _e)
92
+ FileUtils.cp(_s, _e)
93
+ _s, _e = _file('server/config.ru.template'), _file(:'config.ru')
94
+ _l(:renaming, _s, _e)
95
+ FileUtils.mv(_s, _e)
96
+ _s, _e = _file('server/Gemfile'), _file(:'Gemfile')
97
+ _l(:renaming, _s, _e)
98
+ FileUtils.mv(_s, _e)
99
+ _s, _e = _file(:'server/Gemfile.lock'), _file(:'Gemfile.lock')
100
+ _l(:renaming, _s, _e)
101
+ FileUtils.mv(_s, _e)
102
+ File.open(_file(:Rakefile), 'w+') do |f|
103
+ f.puts "load 'shortener/tasks/heroku.rake'"
104
+ end
105
+ end
106
+
107
+ desc "initialize the Git repo"
108
+ task :git do
109
+ cmd = "git init heroku && cd heroku && git add . && git commit -m initial"
110
+ sh cmd
111
+ end
112
+
113
+ desc "update with latest gem files"
114
+ task :update do
115
+ if `git status` =~ /.*working directory clean.*/
116
+ recursively_remove_files(Dir.pwd)
117
+ else
118
+ puts "working directory not clean, stash or commit your changes"
119
+ end
120
+ $existing_repo = true
121
+ Rake::Task[:'short:heroku:build'].execute
122
+ end
123
+
124
+ desc "config a Heroku app the way we need it. Optionally set APPNAME to set heroku app name"
125
+ task :config do
126
+ require 'shortener'
127
+ $name = ENV['APPNAME'] || "shner-#{`whoami`.chomp}"
128
+ cmd = Dir.pwd =~ /heroku$/ ? "" : "cd heroku && "
129
+ cmd += "heroku create #{$name}"
130
+ cmd += " && heroku addons:add redistogo:nano"
131
+ cmd += " && heroku config:add #{Shortener::Configuration.new.to_params}"
132
+ cmd += " && heroku addons:add custom_domains:basic"
133
+ sh cmd
134
+ end
135
+
136
+ desc "Push to Heroku"
137
+ task :push do
138
+ cmd = Dir.pwd =~ /heroku$/ ? "" : "cd heroku && "
139
+ cmd += "git push heroku master"
140
+ sh cmd
141
+ end
142
+
143
+ desc "Build, configure and push a shortener app to Heroku"
144
+ task :setup => [:build, :config, :push] do
145
+ puts "\nYour app has (hopefully) been created and pushed and available @" +
146
+ " http://#{$name}.heroku.com\n\n" +
147
+ "the Custom Domain Addon has been added, but still needs configuring, for" +
148
+ " steps see\n http://devcenter.heroku.com/articles/custom-domains"
149
+ end # => setup
150
+
151
+ end # => heroku
152
+
153
+ namespace :data do
154
+
155
+ desc "replace hyphenated keys with sanitized ones."
156
+ task :dehyphenate_keys do
157
+ require 'shortener'
158
+ redis = Shortener::Configuration.new.redis
159
+ redis.keys("data:*").each do |k|
160
+ hsh = redis.hgetall(k)
161
+ puts "checking #{hsh}" if ENV['VERBOSE']
162
+ set_count, click_count = hsh['set-count'], hsh['click_count']
163
+ arr = Array.new
164
+ arr.concat([:set_count, set_count]) unless set_count.nil?
165
+ arr.concat([:click_count, click_count]) unless click_count.nil?
166
+ puts "** setting: #{arr.inspect}" if ENV['VERBOSE']
167
+ unless arr.empty?
168
+ redis.hmset(k, *arr)
169
+ redis.hdel(k, 'set-count')
170
+ redis.hdel(k, 'click-count')
171
+ end
172
+ puts "#{k} afterwards: #{redis.hgetall(k)}" if ENV['VERBOSE']
173
+ end
174
+ end
175
+
176
+ end # => data
177
+
178
+ end # => short
@@ -1,6 +1,6 @@
1
1
 
2
2
  class Shortener
3
3
 
4
- VERSION = '0.4.3'
4
+ VERSION = '0.5.0'
5
5
 
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: short
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-20 00:00:00.000000000 Z
12
+ date: 2012-02-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sinatra
16
- requirement: &70312062943700 !ruby/object:Gem::Requirement
16
+ requirement: &70210385354960 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70312062943700
24
+ version_requirements: *70210385354960
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: redis-namespace
27
- requirement: &70312062941140 !ruby/object:Gem::Requirement
27
+ requirement: &70210385354320 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70312062941140
35
+ version_requirements: *70210385354320
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: haml
38
- requirement: &70312062940700 !ruby/object:Gem::Requirement
38
+ requirement: &70210385353860 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70312062940700
46
+ version_requirements: *70210385353860
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: turn
49
- requirement: &70312062940220 !ruby/object:Gem::Requirement
49
+ requirement: &70210385351560 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70312062940220
57
+ version_requirements: *70210385351560
58
58
  description: A (hopefully) easy and handy deployable APIable way to shorten links.
59
59
  email:
60
60
  - jake@jakewilkins.com
@@ -101,9 +101,9 @@ files:
101
101
  - lib/shortener/server/views/s3/video.haml
102
102
  - lib/shortener/server/views/upload.haml
103
103
  - lib/shortener/short.rb
104
+ - lib/shortener/tasks/heroku.rake
104
105
  - lib/shortener/version.rb
105
106
  - short.gemspec
106
- - tasks/heroku.rake
107
107
  - test/test_configuration.rb
108
108
  - test/test_server.rb
109
109
  - test/test_short.rb
data/tasks/heroku.rake DELETED
@@ -1,47 +0,0 @@
1
- $gem_dir = File.expand_path(File.dirname(File.dirname(__FILE__)))
2
- def gem_file(*args)
3
- ret = args.map {|f| File.join($gem_dir, f)}.join(" ")
4
- end
5
- namespace :heroku do
6
-
7
- desc "Build a Heroku Ready Git repo"
8
- task :build do
9
- cmd = ""#mkdir heroku"
10
- cmd += "mkdir heroku/server"
11
- cmd += " && cp -r #{gem_file("lib/shortener/server/*")} ./heroku/server/"
12
- cmd += " && cp #{gem_file('lib/shortener/server.rb')} ./heroku/main.rb"
13
- cmd += " && cp #{gem_file('lib/shortener/configuration.rb')} ./heroku/configuration.rb"
14
- cmd += " && mv ./heroku/server/config.ru.template ./heroku/config.ru"
15
- cmd += " && mv ./heroku/server/Gemfile ./heroku/server/Gemfile.lock ./heroku"
16
- cmd += " && git init heroku && cd heroku && git add . && git commit -m initial"
17
- sh cmd
18
- end
19
-
20
- desc "config a Heroku app the way we need it. Optionally set APPNAME to set heroku app name"
21
- task :config do
22
- require_relative '../lib/shortener'
23
- $name = ENV['APPNAME'] || "shner-#{`whoami`.chomp}"
24
- cmd = Dir.pwd =~ /heroku$/ ? "" : "cd heroku && "
25
- cmd += "heroku create #{$name}"
26
- cmd += " && heroku addons:add redistogo:nano"
27
- cmd += " && heroku config:add #{Shortener::Configuration.new.to_params}"
28
- cmd += " && heroku addons:add custom_domains:basic"
29
- sh cmd
30
- end
31
-
32
- desc "Push to Heroku"
33
- task :push do
34
- cmd = Dir.pwd =~ /heroku$/ ? "" : "cd heroku && "
35
- cmd += "git push heroku master"
36
- sh cmd
37
- end
38
-
39
- desc "Build, configure and push a shortener app to Heroku"
40
- task :setup => [:build, :config, :push] do
41
- puts "\nYour app has (hopefully) been created and pushed and available @" +
42
- " http://#{$name}.heroku.com\n\n" +
43
- "the Custom Domain Addon has been added, but still needs configuring, for" +
44
- " steps see\n http://devcenter.heroku.com/articles/custom-domains"
45
- end
46
-
47
- end