Giter Club home page Giter Club logo

shortener's Introduction

<img src=“https://github.com/jpmcgrath/shortener/actions/workflows/ruby.yml/badge.svg” alt=“Build Status” /> <img src=“https://codeclimate.com/github/jpmcgrath/shortener/badges/gpa.svg” /> <img src=“https://badge.fury.io/rb/shortener.svg” alt=“Gem version” />

Shortener

Shortener is a Rails Engine Gem that makes it easy to create and interpret shortened URLs on your own domain from within your Rails application. Once installed Shortener will generate, store URLS and “unshorten” shortened URLs for your applications visitors, all whilst collecting basic usage metrics.

Overview

The majority of the Shortener consists of three parts:

  • a model for storing the details of the shortened link;

  • a controller to accept incoming requests and redirecting them to the target URL;

  • a helper for generating shortened URLs from controllers and views.

Dependencies

Shortener is designed to work from within a Ruby on Rail applications. It has dependancies Rails core components like ActiveRecord, ActionController, the rails routing engine and more.

Ruby Version Support

As Ruby 1.9.3 entered end of maintainance in early 2015, the last version of the Shortener gem to support Ruby 1.9.3 is 0.4.x. Shortener v0.5 onwards will require Ruby 2+.

Upgrading

v0.4.0 to v0.5.0

There have been some breaking changes:

  1. The owner argument is passed into the generator and helper methods as a named parameter.

  2. Original URLs arguments without a hypertext protocol (http|https) will be assumed to be relative paths.

v0.5.1 to v0.5.2

v0.5.2 introduced the ability to set an expiration date for a shortened URL. The expiration dates are stored in a expires_at column in the database, which can be added to your schema with the following migration:

class AddExpiresAtToShortenedUrl < ActiveRecord::Migration[4.2]
  def change
    add_column :shortened_urls, :expires_at, :datetime
  end
end

v0.5.6 to v0.6.1

v0.6.1 introduced the ability to categorize a shortened URL. The category value is stored in a string column in the database, which must be added to your schema with the following migration:

bundle exec rails g migration add_category_to_shortened_url category:string:index

class AddCategoryToShortenedUrl < ActiveRecord::Migration[4.2]
  def change
    add_column :shortened_urls, :category, :string
    add_index :shortened_urls, :category
  end
end

Some niceities of Shortener:

  • The controller does a 301 redirect, which is the recommended type of redirect for maintaining maximum google juice to the original URL;

  • A unique alphanumeric code of generated for each shortened link, this means that we can get more unique combinations than if we just used numbers;

  • The link records a count of how many times it has been “un-shortened”;

  • The link can be associated with a user, this allows for stats of the link usage for a particular user and other interesting things;

Installation

Shortener is compatible with Rails v4, v5, & v6. To install, add to your Gemfile:

gem 'shortener'

After you install Shortener run the generator:

rails generate shortener

This generator will create a migration to create the shortened_urls table where your shortened URLs will be stored.

Note: The default length of the url field in the generated migration is 2083. This is because MySQL requires fixed length indicies and browsers have a defacto limit on URL length. This may not be right for you or your database. The discussion can be seen here github.com/jpmcgrath/shortener/pull/98

Then add to your routes:

get '/:id' => "shortener/shortened_urls#show"

Configuration

The gem can be configured in a config/initializers/shortener.rb file.

By default, the shortener will generate keys that are 5 characters long. You can change that by specifying the length of the key like so;

Shortener.unique_key_length = 6

By default, when a unique key isn’t matched the site is redirected to “/”. You can change that by specifying a different url like so;

Shortener.default_redirect = "http://www.someurl.com"

By default, Shortener will generate unique keys using numbers and lowercase a-z. If you desire more combinations, you can enable the upper and lower case charset, by including the following:

Shortener.charset = :alphanumcase

If you want to use a custom charset, you can create your own combination by creating an array of possible values, such as allowing underscore and dashes:

Shortener.charset = ("a".."z").to_a + (0..9).to_a + ["-", "_"]

By default, Shortener assumes URLs to be valid web URLs and normalizes them in an effort to make sure there are no duplicate records generated for effectively same URLs with differences of only non-effective slash etc. You can control this option if it interferes for any of your logic. One common case is for mobile app links or universal links where normalization can corrupt the URLs of form appname://some_route

Shortener.auto_clean_url = true

Usage

To generate a Shortened URL object for the URL “example.com” within your controller / models do the following:

Shortener::ShortenedUrl.generate("http://example.com")

Alternatively, you can create a shortened url to a relative path within your application:

Shortener::ShortenedUrl.generate("/relative-path?param=whatever")

To generate and display a shortened URL in your application use the helper method:

short_url("http://example.com")

Pass in subdomain, protocol and other options that the UrlHelper url_for accepts:

short_url("http://example.com", url_options: { subdomain: 'foo', host: 'bar', protocol: 'https' } )

This will generate a shortened URL. store it to the db and return a string representing the shortened URL.

Shortened URLs with owner

You can link shortened URLs to an owner, to scope them. To do so, add the following line to the models which will act as owners:

class User < ActiveRecord::Base
  has_shortened_urls
end

This will allow you to pass the owner when generating URLs:

Shortener::ShortenedUrl.generate("example.com", owner: user)

short_url("http://example.com", owner: user)

And to access those URLs:

user.shortened_urls

Shortened URLs with custom unique key

You can pass in your own key when generating a shortened URL. This should be unique.

Important: Custom keys can’t contain characters other than those defined in Shortener.charset. Default is numbers and lowercase a-z (See Configuration).

Shortener::ShortenedUrl.generate("example.com", owner: user, custom_key: "mykey")

short_url("http://example.com", custom_key: 'yourkey')

Expirable Shortened URLs

You can create expirable URLs. Probably, most of the time it would be used with owner:

Shortener::ShortenedUrl.generate("example.com/page", owner: user, expires_at: 24.hours.since)

You can omit owner:

Shortener::ShortenedUrl.generate("example.com/page", expires_at: 24.hours.since)

Sometimes you just need that feeling of a fresh, untouched Shortened URL. By default, Shortener will find an existing ShortenedUrl record for a supplied URL. If you want to create a fresh record, you can pass the following argument:

Shortener::ShortenedUrl.generate("example.com/page", fresh: true)
short_url("http://example.com", fresh: true)

Forbidden keys

You can ensure that records with forbidden keys will not be generated. In Rails you can put next line into config/initializers/shortener.rb

Shortener.forbidden_keys.concat %w(terms promo)

Ignoring Robots

By default Shortener will count all visits to a shortened url, including any crawler robots like the Google Web Crawler, or Twitter’s link unshortening bot. To ignore these visits, Shortener makes use of the excellent voight_kampff gem to identify web robots. This feature is disabled by default. To enable add the following to your shortener configuration:

Shortener.ignore_robots = true

Mounting on a Subdomain

If you want to constrain the shortener route to a subdomain, the following config will prevent the subdomain parameter from leaking in to shortened URLs if it matches the configured subdomain.

Within config/initializers/shortener.rb

Shortener.subdomain = 's'

Within config/routes.rb

constraints subdomain: 's' do
  get '/:id' => "shortener/shortened_urls#show"
end

URL Parameters

Parameters are passed though from the shortened url, to the destination URL. If the destination URL has the same parameters as the destination URL, the parameters on the shortened url take precedence over those on the destination URL.

For example, if we have an orginal URL of: > destination.com?test=yes&happy=defo (identified with token ABCDEF) Which is shortened into: > coolapp.io/s/ABCDEF?test=no&why=not Then, the resulting URL will be: >destination.com?test=no&happy=defo&why=not Note how the test parameter takes the value given on the short URL.

Shorten URLs in generated emails

You can register the included mail interceptor to shorten all links in the emails generated by your Rails app. For example, add to your mailer:

class MyMailer < ActionMailer::Base
  register_interceptor Shortener::ShortenUrlInterceptor.new
end

This will replace all long URLs in the emails generated by MyMailer with shortened versions. The base URL for the shortener will be infered from the mailer’s default_url_options. If you use a different hostname for your shortener, you can use:

class MyMailer < ActionMailer::Base
  register_interceptor Shortener::ShortenUrlInterceptor.new :base_url => "http://shortener.host"
end

The interceptor supports a few more arguments, see the implementation for details.

Logging, stats and other tricks

If you want more things to happen when a user accesses one of your short urls, you can create your own ‘show` action as follows:

def show
  token = ::Shortener::ShortenedUrl.extract_token(params[:id])
  url   = ::Shortener::ShortenedUrl.fetch_with_token(token: token, additional_params: params)
  # do some logging, store some stats
  redirect_to url[:url], status: :moved_permanently
end

Fetch with Token

The `::Shortener::ShortenedUrl.fetch_with_token(token: token, additional_params: params)` does the following:
1. finds the ShortenedUrl for the supplied token
2. increments the use_count for the retrieved ShortenedURL
3. combines the additional parameters with the URL in the retrieved ShortenedURL
4. returns a hash of the format `{url: <the original url>, shortened_url: <the retrieved ShortenedURL>}

*note:* If no shortened URL is found, the url will be `default_redirect` or `/`

Configuring a different database for shortened_urls table

You can store a ‘shortened_urls` table in another database and connecting to it by creating a initializer with the following:

ActiveSupport.on_load(:shortener_record) do
  connects_to(database: { writing: :dbname, reading: :dbname_replica })
end

Note: Please, replace ‘dbname` and `dbname_replica` to match your database configuration.

Configuring Reader and Writer Multi-DB Roles

Shortener has one write operation that happens on a GET request. To allow this you can override this method in an initializer.

module ShortenerWriterMonkeyPatch
  def increment_usage_count
    ActiveRecord::Base.connected_to(role: :writing) do
      self.class.increment_counter(:use_count, id)
    end
  end
end

ActiveSupport.on_load(:shortener_shortened_url) do
  Shortener::ShortenedUrl.prepend(ShortenerWriterMonkeyPatch)
end

Contributing

We welcome new contributors. Because we’re all busy people, and because Shortener is used/relied upon by many projects, it is essential that new Pull Requests are opened with good spec coverage, and a passing build on supported ruby versions and Rails versions. Please create a single PR per feature/contribution, with as few changes per PR as possible to make it easier to review.

To contribute:

  1. Fork it

  2. Create your feature branch (git checkout -b my-new-feature)

  3. Write spec coverage of changes

  4. Commit your changes (git commit -am ‘Add some feature’)

  5. Push to the branch (git push origin my-new-feature)

  6. Create a new Pull Request

  7. Ensure the build is passing

Note: We adhere to the community driven Ruby style guide: github.com/bbatsov/ruby-style-guide

shortener's People

Contributors

asok avatar benjii avatar calward avatar camilleroux avatar codebreach avatar conorsheehan1 avatar craigmcnamara avatar drfeelngood avatar fschwahn avatar gaeduron avatar hughkelsey avatar igor04 avatar jackbot avatar jeffheifetz avatar jonathandean avatar jpmcgrath avatar jweslley avatar mreinsch avatar msquitieri avatar neydroid avatar olleolleolle avatar parterburn avatar petergoldstein avatar pwim avatar saylynt avatar sevaorlov avatar smitp avatar ssbean avatar vitormd avatar ziaulrehman40 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

shortener's Issues

Bad 301 Redirect

My shortened url does bring me to the correct page (ex: onyxla.co/qfohu). However, I'm having issues with it working properly when sharing on Facebook.

Using Facebook's Open Graph Object Debugger, it gives me this error:

object_debugger

Notice that the URL shortener seems to be adding in a "http:/" in the middle of the URL causing Facebook to receive a 404 error for that URL.

Please let me know if this is not an issue with the gem and I can remove this issue.

Thanks!

Failed to migration: Mysql2::Error: Specified key was too long

I use Ruby 2.5.3 / Rails 5.2.1.1

$ rails g shortener
$ rails db:migrate
== 20190206063934 CreateShortenedUrlsTable: migrating =========================
-- adapter_name()
   -> 0.0000s
-- adapter_name()
   -> 0.0000s
-- adapter_name()
   -> 0.0000s
-- create_table(:shortened_urls, {:options=>"ENGINE=InnoDB", :id=>:integer})
   -> 0.1877s
-- add_index(:shortened_urls, :unique_key, {:unique=>true})
   -> 0.1547s
-- add_index(:shortened_urls, :url, {:length=>2083})
rails aborted!
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: Specified key was too long; max key length is 3072 bytes: CREATE  INDEX `index_shortened_urls_on_url`  ON `shortened_urls` (`url`(2083)) 
/Users/******/repositories/******/db/migrate/20190206063934_create_shortened_urls_table.rb:29:in `change'
bin/rails:4:in `<main>'

Caused by:
ActiveRecord::StatementInvalid: Mysql2::Error: Specified key was too long; max key length is 3072 bytes: CREATE  INDEX `index_shortened_urls_on_url`  ON `shortened_urls` (`url`(2083)) 
/Users/******/repositories/******/db/migrate/20190206063934_create_shortened_urls_table.rb:29:in `change'
bin/rails:4:in `<main>'

Caused by:
Mysql2::Error: Specified key was too long; max key length is 3072 bytes
/Users/******/repositories/******/db/migrate/20190206063934_create_shortened_urls_table.rb:29:in `change'
bin/rails:4:in `<main>'
Tasks: TOP => db:migrate

New release? (Rails 6 deprecation warning)

The Rails 6 release candidate gives a deprecation warning for the use of exists? in app/models/shortener/shortened_url.rb:134:in generate_unique_key'`. It says:

DEPRECATION WARNING: Class level methods will no longer inherit scoping from create in Rails 6.1. To continue using the scoped relation, pass it into the block directly. To instead access the full set of models, as Rails 6.1 will, use Shortener::ShortenedUrl.unscoped.

But it looks like this was already updated in the development branch.

Will there be a new release soon?

Upgrade from v 0.3 to 0.5

Hi,

I recently updated by ruby from 1.9.3 to 2.1.8

It is safe to upgrade the gem, Shall I change anything in URL generation, I need to keep all the code as it is now?

Thank you in advance

migration: MySQL requires index length to be specified for column `url`

This is the error I get when I run
rake db:migrate
after having run
rails generate shortener

== CreateShortenedUrlsTable: migrating =======================================
-- create_table(:shortened_urls)
-> 0.0236s
-- add_index(:shortened_urls, :unique_key, {:unique=>true})
-> 0.0164s
-- add_index(:shortened_urls, :url)
rake aborted!
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: BLOB/TEXT column 'url' used in key specification without a key length: CREATE INDEX index_shortened_urls_on_url ON shortened_urls (url)
/.../db/migrate/20160422152339_create_shortened_urls_table.rb:26:in change' ActiveRecord::StatementInvalid: Mysql2::Error: BLOB/TEXT column 'url' used in key specification without a key length: CREATE INDEX index_shortened_urls_on_urlONshortened_urls (url) /.../db/migrate/20160422152339_create_shortened_urls_table.rb:26:in change'
Mysql2::Error: BLOB/TEXT column 'url' used in key specification without a key length
/.../db/migrate/20160422152339_create_shortened_urls_table.rb:26:in `change'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

This is easily corrected by replacing shortener/lib/generators/shortener/templates/migration.rb, line 26 (or the generated migration file) with

    add_index :shortened_urls, :url, :length => { :url => 5 }

I'm using mysql Ver 14.14 Distrib 5.6.29, for osx10.8 (x86_64) using EditLine wrapper.
MySQL only index the N first characters of TEXT or BLOB columns, and requires this index length to be specified. See: MySQL error: key specification without a key length on Stackoverflow.
I don't think the specific index length I used is pertinent, all the more so I only shorten URLs from my own domain, which is longer than 5 characters...
How frequently is this specific index used ? Could I remove it ?

Failed transaction error in rails 4.1.8 and postgres

We are using rails 4.1.8 and Postgres and are receiving the following error when the unique index on the unique_key field is violated:

Shortener::ShortenedUrl Load (0.3ms)  SELECT  "shortened_urls".* FROM "shortened_urls"  WHERE "shortened_urls"."url" = 'http:/hello/boomsdf'  ORDER BY "shortened_urls"."id" ASC LIMIT 1
   (0.1ms)  BEGIN
  SQL (1.0ms)  INSERT INTO "shortened_urls" ("created_at", "unique_key", "updated_at", "url") VALUES ($1, $2, $3, $4) RETURNING "id"  [["created_at", "2015-02-09 16:18:19.247016"], ["unique_key", "g83vq"], ["updated_at", "2015-02-09 16:18:19.247016"], ["url", "http:/hello/boomsdf"]]

PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_shortened_urls_on_unique_key"
DETAIL:  Key (unique_key)=(g83vq) already exists.

: INSERT INTO "shortened_urls" ("created_at", "unique_key", "updated_at", "url") VALUES ($1, $2, $3, $4) RETURNING "id"
retrying with different unique key

  SQL (0.6ms)  INSERT INTO "shortened_urls" ("created_at", "unique_key", "updated_at", "url") VALUES ($1, $2, $3, $4) RETURNING "id"  [["created_at", "2015-02-09 16:18:19.247016"], ["unique_key", "gret4"], ["updated_at", "2015-02-09 16:18:19.247016"], ["url", "http:/hello/boomsdf"]]
PG::InFailedSqlTransaction: ERROR:  current transaction is aborted, commands ignored until end of transaction block
(three more times of this section)
.
.
.
too many retries, giving up

Is this a PG specific error? Has anyone seen it before? As a short term fix we are wrapping our call to ShortenedUrl.generate in a rescue block - it's not pretty.

Returns empty object

Hello everybody,
I'm new to this gem and wanted to build it into my app. So firstly, I started just by creating a shortened url. I tried the following:
Shortener::ShortenedUrl.generate("https://github.com/jpmcgrath/shortener")
However the first SQL Statement instantly rollbacks and I get a an empty object back:

Shortener::ShortenedUrl Load (0.4ms)  SELECT  "shortened_urls".* FROM "shortened_urls" WHERE "shortened_urls"."url" = ? ORDER BY "shortened_urls"."id" ASC LIMIT ? [["url","https://github.com/jpmcgrath/shortener"], ["LIMIT", 1]]
   (0.1ms)  begin transaction
   (0.1ms)  rollback transaction
=> #<Shortener::ShortenedUrl:0x007fc34c4b6f88 id: nil, owner_id: nil, owner_type: nil, url: "https://github.com/jpmcgrath/shortener", unique_key: nil, use_count: 0, expires_at: nil, created_at: nil, updated_at: nil>

Maybe someone can help me on that.. :/

Only counting unshortening by humans

I want to only increase use_count when a real person requests the link. So all bots and auto unshortening services shouldn't be counted. Anyone has an idea how to tackle this?

Consider adopting SemVer

I understand that technically this project complies with SemVer because it is pre 1.0, but introducing breaking changes on minor releases is really bad form. Please consider bumping to a 1.0 release and following SemVer.

forbidden_keys feature

Hi,
thanks for this gem, it made our work easier.

One thing though - in the readme you are mentioning that it is possible to forbid some words from being generate as a unique key. Yet the code does not use this array. Or am I missing something?

I've found commit that does add the feature c788542
But I cannot find a commit that removes the usage of forbidden_keys in the code that generates the unique key.

shortened_urls are not correctly associated to a model with ID type == UUID

in our system, we have a model (object) that is defined with an ID field of type UUID.
i created shortened urls with this object as their owner.
even though different instances of this object were used, some URLs were associated with the same owner.
when i looked at the query that was made by the active record i noticed the following:
SELECT COUNT(*) FROM "shortened_urls" WHERE "shortened_urls"."owner_id" = $1 AND "shortened_urls"."owner_type" = $2�[0m [["owner_id", 0], ["owner_type", "Purchase"]]

notice that the owner ID that is used us '0'.

i then also noticed that the DB migration that is created by the gem creates a table with an 'owner' column of type integer.

apparently, this seems to be the issue.

PG::UndefinedColumn: ERROR: column shortened_urls.expires_at does not exist

I'm using heroku! and the link of app is http://opentxt.herokuapp.com

The app log says the following

2016-02-24T09:18:00.230721+00:00 app[web.1]: 
2016-02-24T09:18:00.230721+00:00 app[web.1]: 
2016-02-24T09:18:00.431872+00:00 app[web.1]: Started GET "/tzktt" for 182.74.246.122 at 2016-02-24 09:18:00 +0000
2016-02-24T09:18:20.167433+00:00 heroku[router]: at=info method=GET path="/tzktt" host=opentxt.herokuapp.com request_id=e74dfaec-2f96-4e13-b495-963fbf7cc98f fwd="182.74.246.122" dyno=web.1 connect=2ms service=12ms status=500 bytes=1754
2016-02-24T09:18:20.181405+00:00 app[web.1]: Started GET "/tzktt" for 182.74.246.122 at 2016-02-24 09:18:20 +0000
2016-02-24T09:18:20.183137+00:00 app[web.1]: Processing by Shortener::ShortenedUrlsController#show as HTML
2016-02-24T09:18:20.183157+00:00 app[web.1]:   Parameters: {"id"=>"tzktt"}
2016-02-24T09:18:20.185874+00:00 app[web.1]: PG::UndefinedColumn: ERROR:  column shortened_urls.expires_at does not exist
2016-02-24T09:18:20.185878+00:00 app[web.1]: LINE 1: ..."shortened_urls".* FROM "shortened_urls"  WHERE (("shortened...
2016-02-24T09:18:20.185880+00:00 app[web.1]:                                                              ^
2016-02-24T09:18:20.185881+00:00 app[web.1]: : SELECT  "shortened_urls".* FROM "shortened_urls"  WHERE (("shortened_urls"."expires_at" IS NULL OR "shortened_urls"."expires_at" > '2016-02-24 09:18:20')) AND "shortened_urls"."unique_key" = 'tzktt'  ORDER BY "shortened_urls"."id" ASC LIMIT 1
2016-02-24T09:18:20.186030+00:00 app[web.1]: Completed 500 Internal Server Error in 3ms
2016-02-24T09:18:20.189092+00:00 app[web.1]: 
2016-02-24T09:18:20.189094+00:00 app[web.1]: ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR:  column shortened_urls.expires_at does not exist
2016-02-24T09:18:20.189096+00:00 app[web.1]: LINE 1: ..."shortened_urls".* FROM "shortened_urls"  WHERE (("shortened...
2016-02-24T09:18:20.189096+00:00 app[web.1]:                                                              ^
2016-02-24T09:18:20.189097+00:00 app[web.1]: : SELECT  "shortened_urls".* FROM "shortened_urls"  WHERE (("shortened_urls"."expires_at" IS NULL OR "shortened_urls"."expires_at" > '2016-02-24 09:18:20')) AND "shortened_urls"."unique_key" = 'tzktt'  ORDER BY "shortened_urls"."id" ASC LIMIT 1):
2016-02-24T09:18:20.189098+00:00 app[web.1]:   vendor/bundle/ruby/2.0.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/postgresql_adapter.rb:822:in `async_exec'

I already did rails generate shortener and code generating link is

<%urll = url_for :controller => 'notes', :action => 'show', :id => notes.id, :host=>request.host %>
<%shorturl = short_url(urll)%>
<%= render_shareable :url=> shorturl, :buttons=> ['twitter', 'facebook', 'google_plus']%>

Show short URL from Shortener::ShortenedUrl object

Is there a method to show the short URL given a Shortener::ShortenedUrl object?

Did not find anything in the code, so currently I'm getting it this way:

shortened_url_object = Shortener::ShortenedUrl.find(foo)
@shortened_url = url_for(controller: :"/shortener/shortened_urls", action: :show, id: shortened_url_object.unique_key, only_path: false)

Rails 5 applications are failing when owner is nil

In a rails 5.2.0 application when you try to generate short url without owner the record is invalid

 bundle exec rails c                                       
Loading development environment (Rails 5.2.0)
irb(main):001:0> Shortener::ShortenedUrl.generate("http://example.com")
   (38.8ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  Shortener::ShortenedUrl Load (29.0ms)  SELECT  `shortened_urls`.* FROM `shortened_urls` WHERE `shortened_urls`.`url` = 'http://example.com/' AND `shortened_urls`.`category` IS NULL ORDER BY `shortened_urls`.`id` ASC LIMIT 1
   (0.3ms)  BEGIN
   (6.1ms)  ROLLBACK
=> #<Shortener::ShortenedUrl id: nil, owner_id: nil, owner_type: nil, url: "http://example.com/", unique_key: nil, category: nil, use_count: 0, expires_at: nil, created_at: nil, updated_at: nil>

Then check if record is valid and the errors

 short_url = Shortener::ShortenedUrl.generate("http://example.com")
  Shortener::ShortenedUrl Load (9.3ms)  SELECT  `shortened_urls`.* FROM `shortened_urls` WHERE `shortened_urls`.`url` = 'http://example.com/' AND `shortened_urls`.`category` IS NULL ORDER BY `shortened_urls`.`id` ASC LIMIT 1
   (0.3ms)  BEGIN
   (0.3ms)  ROLLBACK
=> #<Shortener::ShortenedUrl id: nil, owner_id: nil, owner_type: nil, url: "http://example.com/", unique_key: nil, category: nil, use_count: 0, expires_at: nil, created_at: nil, updated_at: nil>
irb(main):003:0> short_url.valid?
false
irb(main):004:0> short_url.errors
=> #<ActiveModel::Errors:0x00007fee52e58880 @base=#<Shortener::ShortenedUrl id: nil, owner_id: nil, owner_type: nil, url: "http://example.com/", unique_key: nil, category: nil, use_count: 0, expires_at: nil, created_at: nil, updated_at: nil>, @messages={:owner=>["must exist"]}, @details={:owner=>[{:error=>:blank}]}>

It returns that owner must exist which is the expected behavior on Rails 5.x.x

Rails 4 and match

The docs say to add match '/:id' => "shortener/shortened_urls#show" to routes.rb, but this will let the migration fail on Rails 4. It should read "get" instead of "match".

Don't clean_url unless user desires

This is good small library, i integrated in my app to solve a problem mentioned in this SO question, but only to find out that this library doesn't for my use case.

To keep it short, i want to shorten urls to app in form of something like: intent://xyz this is common pattern used for mobile apps, and for API based rails apps which are exposing APIs for their mobile apps.

A quick look suggests issue is here in this line: https://github.com/jpmcgrath/shortener/blob/develop/app/models/shortener/shortened_url.rb#L55
Here it is calling clean_url method, which is sort of pre-processing the URL before saving to DB.

What it ends up being is if i send: myapp://some_path to be shortened, it is saved as /myapp://some_path in DB which obviously doesn't redirect properly. And end up something like: https://mydomain.com/myapp://some_path
which is incorrect.

Suggestion: Disable url_cleaning by default, and make it configurable through initialize. Or vice versa.

I am happy to submit a patch, let me know how you would like it to be. Thanks.
My point

Rails 4.0.0

It doesn't work with Rails 4

I think the problem is with the find_or_create_by_url in the model.

In Rails4 that method is deprecated, you should use find_or_create_by(url: cleaned_url).

With that change, the deprecation warning goes away, but the following error appears:

irb(main):001:0> Shortener::ShortenedUrl.generate("http://dealush.com")
  Shortener::ShortenedUrl Load (0.1ms)  SELECT "shortened_urls".* FROM "shortened_urls" WHERE "shortened_urls"."url" = 'http://dealush.com/' LIMIT 1
  (0.1ms)  begin transaction
  SQL (2.7ms)  INSERT INTO "shortened_urls" ("created_at", "updated_at", "url") VALUES (?, ?, ?)  [["created_at", Tue, 03 Sep 2013 15:52:38 UTC +00:00], ["updated_at", Tue, 03 Sep 2013 15:52:38 UTC +00:00], ["url", "http://dealush.com/"]]
   SQLite3::ConstraintException: shortened_urls.unique_key may not be NULL: INSERT INTO "shortened_urls" ("created_at", "updated_at", "url") VALUES (?, ?, ?)
   (0.1ms)  rollback transaction
   => nil

It seems as if the Model.create method is never called.

Error when run migration

I got this error when run migration and can not proceed.
My MySQL version is 5.7.25

-- create_table(:shortened_urls, {:options=>"ENGINE=InnoDB", :id=>:integer})
   -> 0.1627s
-- add_index(:shortened_urls, :unique_key, {:unique=>true})
   -> 0.0172s
-- add_index(:shortened_urls, :url, {:length=>2083})
rails aborted!
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: Specified key was too long; max key length is 3072 bytes: 
CREATE  INDEX `index_shortened_urls_on_url`  ON `shortened_urls` (`url`(2083))

Faild to add url index faild, it is too long

Hi maintainers,

I installed shortener v0.8.0 on rails 3, run mysql 5.6 (utf8 encoding).

got an error after runing bundle exec rake db:migrate for default migration file

Mysql2::Error: specified key was too long; max key length is 767 bytes

Can shortener delete the url index?

Thanks
JimCheung

Consider providing migrations

Rails engines have the ability to provide migrations, so that on upgrade the user can obtain the latest migrations simply by invoking:

bundle exec rake shortener:install:migrations

Please consider providing such migrations instead of only listing them in the README.

custom_key option does not do anything

I hope I am not just plain stupid here, but I tried and it didn't work, just gave me a random unique_key.

Shortener::ShortenedUrl.generate "http://some.test.com/ruff", custom_key: "ruffruff"

# => #<Shortener::ShortenedUrl:0x007f8a2d43b7a0
# id: 1,
# owner_id: nil,
# owner_type: nil,
# url: "http://some.test.com/ruff",
# unique_key: "o8K3Ih",
# use_count: 0,
# expires_at: nil,
# created_at: Wed, 25 May 2016 02:52:45 UTC +00:00,
# updated_at: Wed, 25 May 2016 02:52:45 UTC +00:00>

Then I tried

Shortener::ShortenedUrl.first_or_create unique_key: "http://some.test.com/ruffuallo", custom_key: "ruffruff2"

#<Shortener::ShortenedUrl:0x007f8a2d493a40
# id: 13,
# owner_id: nil,
# owner_type: nil,
# url: "http://some.test.com/ruffuallo",
# unique_key: "WzHlcU",
# use_count: 0,
# expires_at: nil,
# created_at: Wed, 25 May 2016 02:57:31 UTC +00:00,
# updated_at: Wed, 25 May 2016 02:57:31 UTC +00:00>

Still a random unique_key. After reading the gems code I think this is the problem, in app/models/shortener/shortened_url.rb:84

  define_method CREATE_METHOD_NAME do
    count = 0
    begin
      self.unique_key = generate_unique_key
      #           THIS------^
      super()
      # ...

So we are basically generating the key in the create method, no matter what. ^^

EDIT:
Seems like this has been fixed in the development branch.

Proper way of handling "non unique" unique_key

Currently the code for generating an unique key works fine for application that is run in a single thread/process. More on that in this article.
I think that a better approach would be to just generate a key and attempt to make an insert in a rescue block that would catch any ActiveRecord::RecordNotUnique. The block would have to been retried for a newly generated key.
WDYT? Would you accept a PR with such change?

Add config to let users specify key_length, charset, and actions when links are unshortened.

Hello,

I think this is a great gem but would love to specify the key_length to however long I'd like as well as the charset to increase the number of total possible combinations. It might be best to change the defaults to a key length of 6 and a case sensitive charset so that the total combinations will be over 50 billion, which will cover pretty much any use case.

Also, in addition to saving the number of times the link was unshortened it would be great for users to be able to provide their own callback event to log other information or perform other actions when the link is unshortened.

Thanks!

Checking on unique record is works incorrectly

It use category: :test and url for checking on unique key. Should not.

require 'test_helper'

class ShortenerTest < ActiveSupport::TestCase
  test 'should ignore params for checking on unique' do
    url = 'http://test'
    duplicate_key = 'AAAAAA'
    params = { category: :test, custom_key: duplicate_key }
    Shortener::ShortenedUrl.where(unique_key: duplicate_key).delete_all
    refute_nil Shortener::ShortenedUrl.generate!("#{url}#{rand}", params)
    refute_nil Shortener::ShortenedUrl.generate!("#{url}#{rand}", params)
  end
end

Problem with has_shortened_urls in the model.

Hi.

I went through installation guidelines, but when I add has_shortened_url to my model I can't start server.

I've got this instead:
/Users/nCore/.rvm/gems/ruby-1.9.2-p290@test-project/gems/activerecord-3.2.6/lib/active_record/dynamic_matchers.rb:50:in 'method_missing': undefined local variable or method 'has_shortened_urls' for #<Class:0x007faded9621a0> (NameError)

I'm working on Macbook (OS 10.8.3), Rails 3.2.6, ruby 1.9.2-p290 and Thin rails gem (as webserver).

I tried to turn off Thin, but this doesn't help at all.

Any help will be appreciated.

Shortener generate failed on rails 3

Hi shortener maintainers,

I installed shortener v0.8.0 on rails 3, and got an error after runing rails g shortener

/home/jinhu/.rvm/gems/ruby-2.1.9/gems/shortener-0.8.0/app/controllers/shortener/shortened_urls_controller.rb:2:in `<class:ShortenedUrlsController>': uninitialized constant ActionController::StrongParameters (NameError)
	from /home/jinhu/.rvm/gems/ruby-2.1.9/gems/shortener-0.8.0/app/controllers/shortener/shortened_urls_controller.rb:1:in `<top (required)>'
	from /home/jinhu/.rvm/gems/ruby-2.1.9/gems/railties-3.2.22/lib/rails/engine.rb:444:in `block (2 levels) in eager_load!'

ActionController::StrongParameters is not defined on rails 3

How can I solve this error?

Thanks a lot
jimcheung

returns nil?

Apologies if i have missed the point or the usage but

Shortener::ShortenedUrl.generate("http://dealush.com")

from the command line returns nil? As i say i may have missed something fundamental here, but just trying to understand the usage.

Storing Other details

What if i want to store other details like user's IP or orgin_url's Screenshot using a gem like (IMGKit).

How can i do that.
Thank you

Shortener not generating short url intermittently

I've been having this issue for some time and can't figure out why it could be happening.

This snippet of code fails every now and then. It's executed quite often, so I try to cache the result to avoid hitting the shortener too often. When it fails, I manually run the code in the console and it works just fine, so I'm not seeing a clear pattern here. Any ideas why the generation might be failing?

share_url = "#{Settings.protocol}#{Settings.host}/invite?url=reg/group#{group.id}/inviter#{@user.id}&fbrefresh=#{group.updated_at.to_i}#{group.id}#{@user.id}"
share_url_short = Rails.cache.fetch ["group_user_share_url_#{@user.id}#{group.id}#{group.updated_at.to_i}v3"] do
shortened_url = Shortener::ShortenedUrl.generate(share_url)
begin
"http://#{Settings.short_host}/#{shortened_url.unique_key}"
rescue Exception => e
Rails.cache.delete ["group_user_share_url
#{@user.id}#{group.id}#{group.updated_at.to_i}_v3"]
Bugsnag.notify("Share URL Fail: #{share_url} #{@user.try(:id)} #{e}")
"#{Settings.protocol}#{Settings.host}/invite?url=reg/group#{group.id}"
end
end

setting custom host

I've forked the gem in order to allow setting a custom host for shortened urls. Any reason you can think of this isn't a good idea? Looks like the mailer interceptor allows that, but the "normal" shortening does not.

Using short_url in model

I'm attempting to send a tweet containing shortened post_url. From the view context I can use the short_url method fine but when calling it in the model to prepare the tweet content, it's throwing an error:

ArgumentError: arguments passed to url_for can't be handled. Please require routes or provide your own implementation

def tweet_url
  old_url = post_url(self)    
  Shortener::ShortenedUrl.generate(old_url) 
  new_url = ApplicationController.helpers.short_url(old_url)
  new_url
end

Is there a way to call short_url from a model context?

NoMethodError Shortener::ShortenedUrlsController#show

Hi, Sometimes the logs show the following error Using shortener 0.6.2 and rails '3.2.22.5'

I would like to know how to solve the issue. Thanks!

Sample stack trace (show Rails)
…/shortener-0.6.2/app/models/shortener/
shortened_url.rb:  79:in `extract_token'
…app/controllers/shortener/
/home/deploy/.bundler/pico_sports/ruby/2.1.0/gems/shortener-0.6.2/app/controllers/shortener/shortened_urls_controller.rb
shortened_urls_controller.rb:   7:in `show'
…ts/ruby/2.1.0/gems/journey-1.0.4/lib/journey/
router.rb:  68:in `block in call'
…ts/ruby/2.1.0/gems/journey-1.0.4/lib/journey/
router.rb:  56:in `each'
…ts/ruby/2.1.0/gems/journey-1.0.4/lib/journey/
router.rb:  56:in `call'
…/ruby/2.1.0/gems/rack-1.4.7/lib/rack/
methodoverride.rb:  21:in `call'
…_sports/ruby/2.1.0/gems/rack-1.4.7/lib/rack/
runtime.rb:  17:in `call'
…ico_sports/ruby/2.1.0/gems/rack-1.4.7/lib/rack/
lock.rb:  15:in `call'
…/2.1.0/gems/rack-cache-1.7.0/lib/rack/cache/
context.rb: 140:in `forward'
…/2.1.0/gems/rack-cache-1.7.0/lib/rack/cache/
context.rb: 249:in `fetch'
…/2.1.0/gems/rack-cache-1.7.0/lib/rack/cache/
context.rb: 189:in `lookup'
…/2.1.0/gems/rack-cache-1.7.0/lib/rack/cache/
context.rb:  66:in `call!'
…/2.1.0/gems/rack-cache-1.7.0/lib/rack/cache/
context.rb:  51:in `call'
…by/2.1.0/gems/unicorn-5.2.0/lib/unicorn/
http_server.rb: 562:in `process_client'
…by/2.1.0/gems/unicorn-5.2.0/lib/unicorn/
http_server.rb: 658:in `worker_loop'
…by/2.1.0/gems/unicorn-5.2.0/lib/unicorn/
http_server.rb: 508:in `spawn_missing_workers'
…by/2.1.0/gems/unicorn-5.2.0/lib/unicorn/
http_server.rb: 519:in `maintain_worker_count'
…by/2.1.0/gems/unicorn-5.2.0/lib/unicorn/
http_server.rb: 283:in `join'

owner_type > 20 chars

Hello!

I am running into an issue where the classname of the model that owns a shortened url (via has_shortened_urls) is over 20 characters long and therefore not able to be stored in shortedned_urls table as it is generated by the gem. Is there was a reason the owner_type is limited to 20 chars and will increasing it cause any harm in a production system? I am happy to modify this behavior in my rails project, but before to check in.

Thanks in advance and love the gem!

UrlInterceptor does not render namespaced Short URLs

Let's say you defined

get '/short_urls/:id' => "shortener/shortened_urls#show"

in config/routes.rb, and you had a Mailer with

register_interceptor Shortener::ShortenUrlInterceptor.new

after rendering the email you would see URLs like http://<host>/fadfa instead of http://<host>/short_urls/fadfa.

I "monkey-patched" this as follows: kennym@0d75d10
but this doesn't feel right, despite doing its job.

Maybe get inspired by Devise's devise_for method?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.