rails_url_shortener 0.1.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +92 -0
- data/Rakefile +8 -0
- data/app/controllers/rails_url_shortener/urls_controller.rb +14 -0
- data/app/helpers/rails_url_shortener/application_helper.rb +4 -0
- data/app/helpers/rails_url_shortener/urls_helper.rb +28 -0
- data/app/models/rails_url_shortener/application_record.rb +5 -0
- data/app/models/rails_url_shortener/url.rb +88 -0
- data/app/models/rails_url_shortener/visit.rb +31 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20220407202526_create_rails_url_shortener_urls.rb +22 -0
- data/db/migrate/20220407202539_create_rails_url_shortener_visits.rb +24 -0
- data/lib/generators/rails_url_shortener/rails_url_shortener_generator.rb +11 -0
- data/lib/generators/rails_url_shortener/templates/initializer.rb +11 -0
- data/lib/rails_url_shortener/engine.rb +6 -0
- data/lib/rails_url_shortener/version.rb +3 -0
- data/lib/rails_url_shortener.rb +37 -0
- data/lib/tasks/rails_url_shortener_tasks.rake +4 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '0847fa49aa14c40889c11cdf80fd32a846208112f7b1e121455f6c6258225cf8'
|
4
|
+
data.tar.gz: 2479208e471351f49be2f9876f33ce22cc6cf6461c62113b0d5e5c66d8bd884a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 94658a6b36105a31e1abc8ce7ac9b67913e2fecec29a834f9898224b1969ac0f593ee72c157bbe149e75a439fe5d68efaa167aebc83758fdd85eb578aef3b047
|
7
|
+
data.tar.gz: f40c5f6dc58ab19cd03bd714db4d68431889733579dce5e61c8990964fdc82211cc8d95e20d36d54bdc6909b7f93e871ae3648bd21ab5977123c7c93f2a71fed
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022 a-chacon
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# RailsUrlShortener
|
2
|
+
|
3
|
+
A small rails engine for short urls.
|
4
|
+
It could be used like a url shortener or a ip logger, it is your choice.
|
5
|
+
The app generate a short url for you and then (if you want) receive the requests and redirect to the original url.
|
6
|
+
|
7
|
+
Why give your data to a third party app if you can do it by yourself?
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
Mount the controller on your app adding the next code on your config/routes.rb:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
mount RailsUrlShortener::Engine, at: "/"
|
15
|
+
|
16
|
+
```
|
17
|
+
|
18
|
+
And generate the short links wherever you want using the helper method:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
short_url("https://www.github.com/a-chacon/rails_url_shortener")
|
22
|
+
```
|
23
|
+
|
24
|
+
or model method:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
RailsUrlShortener::Url.generate("https://www.github.com/a-chacon/rails_url_shortener")
|
28
|
+
```
|
29
|
+
|
30
|
+
**Then share the short link.**
|
31
|
+
|
32
|
+
### Deeper
|
33
|
+
|
34
|
+
By default this engine save all request made on your short url, you can use that data for some analitics or simple ip logger. So for get the data in a controller or do wherever you want you can use the Visit model related to a Url:
|
35
|
+
```ruby
|
36
|
+
RailsUrlShortener::Url.find_by_key("key").visits # all visits
|
37
|
+
|
38
|
+
```
|
39
|
+
Or using the model class:
|
40
|
+
```ruby
|
41
|
+
RailsUrlShortener::Visit.all # all in database
|
42
|
+
```
|
43
|
+
|
44
|
+
Also the Url model has a polymorphic relation with an owner that is optional. So you can relate an url whatever you want in your app adding the next relation in a model:
|
45
|
+
```ruby
|
46
|
+
has_many :urls, as: :owner
|
47
|
+
```
|
48
|
+
|
49
|
+
## Installation
|
50
|
+
|
51
|
+
Add this line to your application's Gemfile:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
gem "rails_url_shortener"
|
55
|
+
```
|
56
|
+
|
57
|
+
Or install it yourself as:
|
58
|
+
```bash
|
59
|
+
gem install rails_url_shortener
|
60
|
+
```
|
61
|
+
|
62
|
+
Then execute:
|
63
|
+
```bash
|
64
|
+
bundle
|
65
|
+
```
|
66
|
+
|
67
|
+
And finally install the migrations on your project and migrate:
|
68
|
+
```bash
|
69
|
+
bin/rails rails_url_shortener:install:migrations db:migrate
|
70
|
+
```
|
71
|
+
|
72
|
+
If you want the initializer for configurations do:
|
73
|
+
|
74
|
+
```bash
|
75
|
+
rails generate RailsUrlShortener:initializer
|
76
|
+
```
|
77
|
+
|
78
|
+
## Contributing
|
79
|
+
|
80
|
+
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
|
81
|
+
|
82
|
+
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
|
83
|
+
Don't forget to give the project a star! Thanks again!
|
84
|
+
|
85
|
+
1. Fork the Project
|
86
|
+
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
87
|
+
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
88
|
+
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
89
|
+
5. Open a Pull Request
|
90
|
+
|
91
|
+
## License
|
92
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module RailsUrlShortener
|
2
|
+
class UrlsController < ActionController::Metal
|
3
|
+
include ActionController::StrongParameters
|
4
|
+
include ActionController::Redirecting
|
5
|
+
include ActionController::Instrumentation
|
6
|
+
include Rails.application.routes.url_helpers
|
7
|
+
|
8
|
+
def show
|
9
|
+
# find, if you pass the request then this is saved
|
10
|
+
url = Url.find_by_key(params[:key], request: request)
|
11
|
+
redirect_to url.url, status: :moved_permanently
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RailsUrlShortener
|
2
|
+
module UrlsHelper
|
3
|
+
def short_url(url, owner: nil, key: nil, expires_at: nil, category: nil, url_options: {})
|
4
|
+
url_object = Url.generate(
|
5
|
+
url,
|
6
|
+
owner: owner,
|
7
|
+
key: key,
|
8
|
+
expires_at: expires_at,
|
9
|
+
category: category
|
10
|
+
)
|
11
|
+
|
12
|
+
if url_object.errors.empty?
|
13
|
+
# This must be fixed
|
14
|
+
# the url_for helper must generate the url
|
15
|
+
# options = {
|
16
|
+
# controller: "rails_url_shortener_url/urls",
|
17
|
+
# action: "show",
|
18
|
+
# key: url.key
|
19
|
+
# }.merge(url_options)
|
20
|
+
|
21
|
+
# url_for(options)
|
22
|
+
rails_url_shortener_url + url_object.key
|
23
|
+
else
|
24
|
+
url
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module RailsUrlShortener
|
2
|
+
class Url < ApplicationRecord
|
3
|
+
# variables
|
4
|
+
attr_accessor :generating_retries, :key_length
|
5
|
+
|
6
|
+
# relations
|
7
|
+
belongs_to :owner, polymorphic: true, optional: true
|
8
|
+
has_many :visits
|
9
|
+
|
10
|
+
# validations
|
11
|
+
validates :key, presence: true, length: { minimum: RailsUrlShortener.minimum_key_length }, uniqueness: true
|
12
|
+
validates :url, presence: true, format: URI::DEFAULT_PARSER.make_regexp(%w[http https])
|
13
|
+
|
14
|
+
# exclude records in which expiration time is set and expiration time is greater than current time
|
15
|
+
scope :unexpired, -> { where(arel_table[:expires_at].eq(nil).or(arel_table[:expires_at].gt(::Time.current))) }
|
16
|
+
|
17
|
+
# callbacks
|
18
|
+
before_validation :generate_key
|
19
|
+
after_initialize :set_attr
|
20
|
+
|
21
|
+
##
|
22
|
+
# set default instance variables values
|
23
|
+
def set_attr
|
24
|
+
@generating_retries = 0
|
25
|
+
@key_length = RailsUrlShortener.key_length
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# create a url object with the given params
|
30
|
+
#
|
31
|
+
# if something is wrong return the object with errors
|
32
|
+
|
33
|
+
def self.generate(url, owner: nil, key: nil, expires_at: nil, category: nil)
|
34
|
+
create(
|
35
|
+
url: url,
|
36
|
+
owner: owner,
|
37
|
+
key: key,
|
38
|
+
expires_at: expires_at,
|
39
|
+
category: category
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# find a Url object by the key param
|
45
|
+
#
|
46
|
+
# if the Url is not found an exception is raised
|
47
|
+
## TODO: and pass query params
|
48
|
+
def self.find_by_key!(key, request: nil)
|
49
|
+
# Get the token if not exipired
|
50
|
+
url = Url.unexpired.find_by!(key: key)
|
51
|
+
Visit.parse_and_save(url, request) unless request.nil?
|
52
|
+
url
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# find a Url object by the key param
|
57
|
+
#
|
58
|
+
# if the Url is not found the exception is rescue and
|
59
|
+
# return a new url object with the default url
|
60
|
+
|
61
|
+
def self.find_by_key(key, request: nil)
|
62
|
+
find_by_key!(key, request: request)
|
63
|
+
rescue ActiveRecord::RecordNotFound
|
64
|
+
Url.new(
|
65
|
+
url: RailsUrlShortener.default_redirect || '/',
|
66
|
+
key: 'none'
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def key_candidate
|
73
|
+
(0...key_length).map { RailsUrlShortener.charset[rand(RailsUrlShortener.charset.size)] }.join
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate_key
|
77
|
+
if key.nil?
|
78
|
+
loop do
|
79
|
+
# plus to the key length if after 10 attempts was not found a candidate
|
80
|
+
self.key_length += 1 if generating_retries >= 10
|
81
|
+
self.key = key_candidate
|
82
|
+
self.generating_retries += 1
|
83
|
+
break unless self.class.exists?(key: key)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RailsUrlShortener
|
2
|
+
require 'json'
|
3
|
+
class Visit < ApplicationRecord
|
4
|
+
belongs_to :url
|
5
|
+
|
6
|
+
##
|
7
|
+
# Parse a request information and save
|
8
|
+
#
|
9
|
+
# Return boolean
|
10
|
+
|
11
|
+
def self.parse_and_save(url, request)
|
12
|
+
# browser detection
|
13
|
+
browser = Browser.new(request.headers['User-Agent'])
|
14
|
+
if !RailsUrlShortener.save_bots_visits && browser.bot?
|
15
|
+
false
|
16
|
+
else
|
17
|
+
# save
|
18
|
+
Visit.create(
|
19
|
+
url: url,
|
20
|
+
ip: request.ip,
|
21
|
+
browser: browser.name,
|
22
|
+
browser_version: browser.full_version,
|
23
|
+
platform: browser.platform.name,
|
24
|
+
platform_version: browser.platform.version,
|
25
|
+
bot: browser.bot?,
|
26
|
+
user_agent: request.headers['User-Agent']
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
class CreateRailsUrlShortenerUrls < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :rails_url_shortener_urls do |t|
|
4
|
+
# optional if you can link it to a user or other model
|
5
|
+
t.references :owner, polymorphic: true, null: true
|
6
|
+
|
7
|
+
# the real url
|
8
|
+
t.text :url, null: false, length: 2048
|
9
|
+
|
10
|
+
# the unique key
|
11
|
+
t.string :key, limit: 10, null: false
|
12
|
+
|
13
|
+
# category for short url
|
14
|
+
t.string :category
|
15
|
+
|
16
|
+
# valid until date for expirable urls
|
17
|
+
t.datetime :expires_at
|
18
|
+
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class CreateRailsUrlShortenerVisits < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :rails_url_shortener_visits do |t|
|
4
|
+
t.belongs_to :url
|
5
|
+
# client ip
|
6
|
+
t.string :ip
|
7
|
+
# browser from user_agent
|
8
|
+
t.string :browser
|
9
|
+
# browser version from user_agent
|
10
|
+
t.string :browser_version
|
11
|
+
# platform from user_agent
|
12
|
+
t.string :platform
|
13
|
+
# platform version from user_agent
|
14
|
+
t.string :platform_version
|
15
|
+
# if the request was a bot or not
|
16
|
+
t.boolean :bot
|
17
|
+
t.string :user_agent
|
18
|
+
# variable where we save all data that can be catch from the request
|
19
|
+
t.text :meta
|
20
|
+
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
CHARSETS = {
|
2
|
+
alphanum: ('a'..'z').to_a + (0..9).to_a,
|
3
|
+
alphacase: ('a'..'z').to_a + ('A'..'Z').to_a,
|
4
|
+
alphanumcase: ('A'..'Z').to_a + ('a'..'z').to_a + (0..9).to_a
|
5
|
+
}
|
6
|
+
|
7
|
+
RailsUrlShortener.default_redirect = "/"
|
8
|
+
RailsUrlShortener.charset = CHARSETS[:alphanumcase]
|
9
|
+
RailsUrlShortener.key_length = 6
|
10
|
+
RailsUrlShortener.minimum_key_length = 3
|
11
|
+
RailsUrlShortener.save_bots_visits = false
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "rails_url_shortener/version"
|
2
|
+
require "rails_url_shortener/engine"
|
3
|
+
|
4
|
+
module RailsUrlShortener
|
5
|
+
|
6
|
+
##
|
7
|
+
# constants
|
8
|
+
CHARSETS = {
|
9
|
+
alphanum: ('a'..'z').to_a + (0..9).to_a,
|
10
|
+
alphacase: ('a'..'z').to_a + ('A'..'Z').to_a,
|
11
|
+
alphanumcase: ('A'..'Z').to_a + ('a'..'z').to_a + (0..9).to_a
|
12
|
+
}
|
13
|
+
|
14
|
+
##
|
15
|
+
# default redirection url when the key isn't found
|
16
|
+
mattr_accessor :default_redirect, default: '/'
|
17
|
+
|
18
|
+
##
|
19
|
+
# charset for generate keys
|
20
|
+
mattr_accessor :charset, default: CHARSETS[:alphanumcase]
|
21
|
+
|
22
|
+
##
|
23
|
+
# default key length used by random keys
|
24
|
+
mattr_accessor :key_length, default: 6
|
25
|
+
|
26
|
+
##
|
27
|
+
# minimum key length for custom keys
|
28
|
+
mattr_accessor :minimum_key_length, default: 3
|
29
|
+
|
30
|
+
##
|
31
|
+
# if save bots visits on db, the detection is provided by browser gem
|
32
|
+
# and is described like "The bot detection is quite aggressive"
|
33
|
+
# so if you put this configuration like false could lose some visits to your link
|
34
|
+
# by default saving all requests
|
35
|
+
mattr_accessor :save_bots_visits, default: true
|
36
|
+
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_url_shortener
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- a-chacon
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-04-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 7.0.2.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 7.0.2.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: browser
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 5.3.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 5.3.0
|
41
|
+
description: A little engine for rails application that provide url shortener functions.
|
42
|
+
email:
|
43
|
+
- andres.ch@protonmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- MIT-LICENSE
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- app/controllers/rails_url_shortener/urls_controller.rb
|
52
|
+
- app/helpers/rails_url_shortener/application_helper.rb
|
53
|
+
- app/helpers/rails_url_shortener/urls_helper.rb
|
54
|
+
- app/models/rails_url_shortener/application_record.rb
|
55
|
+
- app/models/rails_url_shortener/url.rb
|
56
|
+
- app/models/rails_url_shortener/visit.rb
|
57
|
+
- config/routes.rb
|
58
|
+
- db/migrate/20220407202526_create_rails_url_shortener_urls.rb
|
59
|
+
- db/migrate/20220407202539_create_rails_url_shortener_visits.rb
|
60
|
+
- lib/generators/rails_url_shortener/rails_url_shortener_generator.rb
|
61
|
+
- lib/generators/rails_url_shortener/templates/initializer.rb
|
62
|
+
- lib/rails_url_shortener.rb
|
63
|
+
- lib/rails_url_shortener/engine.rb
|
64
|
+
- lib/rails_url_shortener/version.rb
|
65
|
+
- lib/tasks/rails_url_shortener_tasks.rake
|
66
|
+
homepage: https://www.github.com/a-chacon/rails_url_shortener
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata:
|
70
|
+
homepage_uri: https://www.github.com/a-chacon/rails_url_shortener
|
71
|
+
source_code_uri: https://www.github.com/a-chacon/rails_url_shortener
|
72
|
+
changelog_uri: https://www.github.com/a-chacon/rails_url_shortener
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubygems_version: 3.2.22
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Rails url shortener engine.
|
92
|
+
test_files: []
|