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 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,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -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,4 @@
1
+ module RailsUrlShortener
2
+ module ApplicationHelper
3
+ end
4
+ 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,5 @@
1
+ module RailsUrlShortener
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ 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,3 @@
1
+ RailsUrlShortener::Engine.routes.draw do
2
+ get '/:key', to: 'urls#show'
3
+ end
@@ -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
+ require 'rails/generators'
2
+
3
+ class RailsUrlShortenerGenerator < Rails::Generators::Base
4
+
5
+ source_root File.expand_path("templates", __dir__)
6
+
7
+ def copy
8
+ copy_file "initializer.rb", "config/initializers/rails_url_shortener.rb"
9
+ end
10
+
11
+ 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,6 @@
1
+ module RailsUrlShortener
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RailsUrlShortener
4
+ require 'browser'
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module RailsUrlShortener
2
+ VERSION = "0.1.0"
3
+ end
@@ -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
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rails_url_shortener do
3
+ # # Task goes here
4
+ # 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: []