seo_optimizer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 415bd41bed30c7e261ee469383cc1384e07fe41bcd1ac7e0da7b08811e3875ac
4
+ data.tar.gz: d45bd5f93a75a9ba0758372c3d5cb26c1fd01be470097d494f614efe2e654163
5
+ SHA512:
6
+ metadata.gz: e3ffb4b684021306c18991d6c35d6f024001f84f6f0b4225bf60b5fe91fcc6047c0ed168ae72ef229664ebf6c96bf9f5cb1bcd7a23eb2243ef93daf20e7fc986
7
+ data.tar.gz: bea7e285cdf9c260b748867a27ee1c104107543fe7892f9ff34ebe6b74fd38c78aa29f8302d198e26f54b9c9f3ba6c1d758f92c23a7cd1ee3d49e33f89c74dcd
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .idea/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1 @@
1
+ 2.7.1
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.1
6
+ before_install: gem install bundler -v 2.1.4
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at ronan33720@hotmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in seo_optimizer.gemspec
4
+ gemspec
5
+
6
+ gem 'rake', '~> 12.0'
7
+ gem 'rspec', '~> 3.0'
8
+ gem 'sitemap_generator'
@@ -0,0 +1,39 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ seo_optimizer (0.1.0)
5
+ sitemap_generator
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ builder (3.2.4)
11
+ diff-lcs (1.4.4)
12
+ rake (12.3.3)
13
+ rspec (3.10.0)
14
+ rspec-core (~> 3.10.0)
15
+ rspec-expectations (~> 3.10.0)
16
+ rspec-mocks (~> 3.10.0)
17
+ rspec-core (3.10.0)
18
+ rspec-support (~> 3.10.0)
19
+ rspec-expectations (3.10.0)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.10.0)
22
+ rspec-mocks (3.10.0)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.10.0)
25
+ rspec-support (3.10.0)
26
+ sitemap_generator (6.1.2)
27
+ builder (~> 3.0)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ rake (~> 12.0)
34
+ rspec (~> 3.0)
35
+ seo_optimizer!
36
+ sitemap_generator
37
+
38
+ BUNDLED WITH
39
+ 2.1.4
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Ronan Louarn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,166 @@
1
+ # Seo Optimizer
2
+
3
+ Seo Optimizer provides a set of helpers which guide you in SEO improvements.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'seo_optimizer'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install seo_optimizer
20
+
21
+ ## Usage
22
+
23
+ #### Sitemap.yml, robots.txt
24
+
25
+ There is a generator and a task to create needed files that will be read by search engines robots.
26
+
27
+ **1** - Setup (it add a new config variable in `application.rb`)
28
+
29
+ ```bash
30
+ $ rails g seo_optimizer:setups
31
+ ```
32
+ **2** - Replace the default value of product_url in `application.rb`
33
+
34
+ ```ruby
35
+ config.sitemap_config_variable = {}
36
+ config.sitemap_config_variable[:production_url] = 'https://YOUR-PRODUCTION-URL.com'
37
+ ```
38
+
39
+ **3** - Generate robots files (this will create sitemap.yml, robots.txt routes/actions/views)
40
+
41
+ ```bash
42
+ $ rails g seo_optimizer:robots_files
43
+ ```
44
+
45
+ **4** - Edit sitemap generate task `lib/tasks/sitemap/generate.rake` to add your customs routes like resources CRUD
46
+ routes etc, check the section below for more informations or check sitemap_generator
47
+ doc (https://github.com/kjvarga/sitemap_generator).
48
+
49
+ **5** - Generate the sitemap.xml
50
+ ```bash
51
+ $ rails sitemap:generate
52
+ ```
53
+ **Existing routes will be generated in sitemap by default.**
54
+
55
+ Then you can check these urls:
56
+
57
+ robots.txt file: http://localhost:3000/robots.txt
58
+ sitemap.xml file: http://localhost:3000/sitemap.xml
59
+
60
+ _________________
61
+
62
+ #### Model routes with slug:
63
+
64
+ To add seo_slug field to a model, you can use the generator as shown below.
65
+
66
+ **1** - Syntax:
67
+ `rails g seo_optimizer:my_model field_1 field_2 3 4 5...`
68
+
69
+ $ rails generate seo_optimizer:user first_name last_name other_field
70
+
71
+ **2** - Migrate
72
+
73
+ $ rails db:migrate
74
+
75
+ **3** - Override `to_param` method in your model to change your models links pattern
76
+
77
+ ````ruby
78
+ class User < ApplicationRecord
79
+ ...
80
+
81
+ def to_param
82
+ seo_slug
83
+ end
84
+ end
85
+ ````
86
+
87
+ And then `<%= link_to 'Show', User.first %>` will generate a link that follow the new seo pattern
88
+
89
+ `/users/USERID-FIELD-FIELD-FIELD`
90
+
91
+
92
+ `/users/1-field_one-field_two-other_field`
93
+
94
+
95
+ **4** - So, in your `UsersController`:
96
+
97
+ ```ruby
98
+ class UsersController < ApplicationController
99
+ before_action :set_user, only: :show
100
+
101
+ def show; end
102
+
103
+ private
104
+
105
+ def set_user
106
+ # we find users by seo_slug field
107
+ @user = User.find_by(seo_slug: params[:id])
108
+ end
109
+ end
110
+ ```
111
+
112
+ **5** - You can update the sitemap task to add every routes with slugs in it.
113
+
114
+ - In `SitemapGenerator::Sitemap.create` block
115
+
116
+ ```ruby
117
+ SitemapGenerator::Sitemap.create do
118
+ ...
119
+ User.select(:seo_slug, :updated_at).each do |slug|
120
+ add user_path(slug), lastmod: slug.updated_at, priority: 0.8
121
+ end
122
+ end
123
+ ```
124
+
125
+ - Then don't forget to `$ rails sitemap:generate`.
126
+
127
+ ______________
128
+
129
+ #### Error pages (404, 500):
130
+
131
+ **1** - Generate error pages view, actions and routes.
132
+ ```bash
133
+ $ rails g seo_optimizer:errors_pages
134
+ ```
135
+
136
+ **2** - You can customize pages in `app/views/errors/`
137
+
138
+ **3** - Edit `config/environment/develoment.rb` if you want to test it in dev environment.
139
+
140
+ ⛔️ DO NOT COMMIT ⛔️
141
+
142
+ ```ruby
143
+ config.consider_all_requests_local = true
144
+ # to
145
+ config.consider_all_requests_local = false
146
+ ```
147
+ _________________
148
+
149
+ ## Development
150
+
151
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
152
+
153
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
154
+
155
+ ## Contributing
156
+
157
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/seo_optimizer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/seo_optimizer/blob/master/CODE_OF_CONDUCT.md).
158
+
159
+
160
+ ## License
161
+
162
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
163
+
164
+ ## Code of Conduct
165
+
166
+ Everyone interacting in the SeoOptimizer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/seo_optimizer/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "seo_optimizer"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/migration'
4
+
5
+ module SeoOptimizer
6
+ class ErrorsPagesGenerator < Rails::Generators::Base
7
+ source_root File.expand_path('templates', __dir__)
8
+ desc 'Generates errors pages'
9
+
10
+ def create_controller_files
11
+ route %(get '/500' => 'errors#internal_server_error'\nget '/404' => 'errors#not_found'\n)
12
+
13
+ inject_into_file 'config/application.rb', :before => " end" do
14
+ "\n config.exceptions_app = self.routes\n"
15
+ end
16
+ %x(rm -f public/404.html)
17
+ %x(rm -f public/500.html)
18
+ template 'errors_controller_template.erb', File.join('app/controllers', 'errors_controller.rb')
19
+ template '404.erb', File.join('app/views/errors', 'not_found.html.erb')
20
+ template '500.erb', File.join('app/views/errors', 'internal_server_error.html.erb')
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,231 @@
1
+ <div class="background">
2
+ <div class="ground"></div>
3
+ </div>
4
+ <div class="container">
5
+ <div class="left-section">
6
+ <div class="inner-content">
7
+ <h1 class="heading">404</h1>
8
+ <p class="subheading">Looks like the page you were looking for is no longer here.</p>
9
+ </div>
10
+ </div>
11
+ <div class="right-section">
12
+ <svg class="svgimg" xmlns="http://www.w3.org/2000/svg" viewBox="51.5 -15.288 385 505.565">
13
+ <g class="bench-legs">
14
+ <path d="M202.778,391.666h11.111v98.611h-11.111V391.666z M370.833,390.277h11.111v100h-11.111V390.277z M183.333,456.944h11.111
15
+ v33.333h-11.111V456.944z M393.056,456.944h11.111v33.333h-11.111V456.944z" />
16
+ </g>
17
+ <g class="top-bench">
18
+ <path d="M396.527,397.917c0,1.534-1.243,2.777-2.777,2.777H190.972c-1.534,0-2.778-1.243-2.778-2.777v-8.333
19
+ c0-1.535,1.244-2.778,2.778-2.778H393.75c1.534,0,2.777,1.243,2.777,2.778V397.917z M400.694,414.583
20
+ c0,1.534-1.243,2.778-2.777,2.778H188.194c-1.534,0-2.778-1.244-2.778-2.778v-8.333c0-1.534,1.244-2.777,2.778-2.777h209.723
21
+ c1.534,0,2.777,1.243,2.777,2.777V414.583z M403.473,431.25c0,1.534-1.244,2.777-2.778,2.777H184.028
22
+ c-1.534,0-2.778-1.243-2.778-2.777v-8.333c0-1.534,1.244-2.778,2.778-2.778h216.667c1.534,0,2.778,1.244,2.778,2.778V431.25z"
23
+ />
24
+ </g>
25
+ <g class="bottom-bench">
26
+ <path d="M417.361,459.027c0,0.769-1.244,1.39-2.778,1.39H170.139c-1.533,0-2.777-0.621-2.777-1.39v-4.86
27
+ c0-0.769,1.244-0.694,2.777-0.694h244.444c1.534,0,2.778-0.074,2.778,0.694V459.027z" />
28
+ <path d="M185.417,443.75H400c0,0,18.143,9.721,17.361,10.417l-250-0.696C167.303,451.65,185.417,443.75,185.417,443.75z" />
29
+ </g>
30
+ <g id="lamp">
31
+ <path class="lamp-details" d="M125.694,421.997c0,1.257-0.73,3.697-1.633,3.697H113.44c-0.903,0-1.633-2.44-1.633-3.697V84.917
32
+ c0-1.257,0.73-2.278,1.633-2.278h10.621c0.903,0,1.633,1.02,1.633,2.278V421.997z"
33
+ />
34
+ <path class="lamp-accent" d="M128.472,93.75c0,1.534-1.244,2.778-2.778,2.778h-13.889c-1.534,0-2.778-1.244-2.778-2.778V79.861
35
+ c0-1.534,1.244-2.778,2.778-2.778h13.889c1.534,0,2.778,1.244,2.778,2.778V93.75z" />
36
+
37
+ <circle class="lamp-light" cx="119.676" cy="44.22" r="40.51" />
38
+ <path class="lamp-details" d="M149.306,71.528c0,3.242-13.37,13.889-29.861,13.889S89.583,75.232,89.583,71.528c0-4.166,13.369-13.889,29.861-13.889
39
+ S149.306,67.362,149.306,71.528z"/>
40
+ <radialGradient class="light-gradient" id="SVGID_1_" cx="119.676" cy="44.22" r="65" gradientUnits="userSpaceOnUse">
41
+ <stop offset="0%" style="stop-color:#FFFFFF; stop-opacity: 1"/>
42
+ <stop offset="50%" style="stop-color:#EDEDED; stop-opacity: 0.5">
43
+ <animate attributeName="stop-opacity" values="0.0; 0.5; 0.0" dur="5000ms" repeatCount="indefinite"></animate>
44
+ </stop>
45
+ <stop offset="100%" style="stop-color:#EDEDED; stop-opacity: 0"/>
46
+ </radialGradient>
47
+ <circle class="lamp-light__glow" fill="url(#SVGID_1_)" cx="119.676" cy="44.22" r="65"/>
48
+ <path class="lamp-bottom" d="M135.417,487.781c0,1.378-1.244,2.496-2.778,2.496H106.25c-1.534,0-2.778-1.118-2.778-2.496v-74.869
49
+ c0-1.378,1.244-2.495,2.778-2.495h26.389c1.534,0,2.778,1.117,2.778,2.495V487.781z" />
50
+ </g>
51
+ </svg>
52
+ </div>
53
+ </div>
54
+
55
+ <style>
56
+ @import url("https://fonts.googleapis.com/css?family=Fira+Sans");
57
+ /*Variables */
58
+ .left-section .inner-content {
59
+ position: absolute;
60
+ top: 50%;
61
+ transform: translateY(-50%);
62
+ }
63
+
64
+ * {
65
+ box-sizing: border-box;
66
+ }
67
+
68
+ html, body {
69
+ margin: 0;
70
+ padding: 0;
71
+ }
72
+
73
+ body {
74
+ font-family: "Fira Sans", sans-serif;
75
+ color: #f5f6fa;
76
+ }
77
+
78
+ .background {
79
+ position: absolute;
80
+ top: 0;
81
+ left: 0;
82
+ width: 100%;
83
+ height: 100%;
84
+ background: linear-gradient(#0C0E10, #446182);
85
+ }
86
+ .background .ground {
87
+ position: absolute;
88
+ bottom: 0;
89
+ width: 100%;
90
+ height: 25vh;
91
+ background: #0C0E10;
92
+ }
93
+ @media (max-width: 770px) {
94
+ .background .ground {
95
+ height: 0vh;
96
+ }
97
+ }
98
+
99
+ .container {
100
+ position: relative;
101
+ margin: 0 auto;
102
+ width: 85%;
103
+ height: 100vh;
104
+ padding-bottom: 25vh;
105
+ display: flex;
106
+ flex-direction: row;
107
+ justify-content: space-around;
108
+ }
109
+ @media (max-width: 770px) {
110
+ .container {
111
+ flex-direction: column;
112
+ padding-bottom: 0vh;
113
+ }
114
+ }
115
+
116
+ .left-section, .right-section {
117
+ position: relative;
118
+ }
119
+
120
+ .left-section {
121
+ width: 40%;
122
+ }
123
+ @media (max-width: 770px) {
124
+ .left-section {
125
+ width: 100%;
126
+ height: 40%;
127
+ position: absolute;
128
+ top: 0;
129
+ }
130
+ }
131
+ @media (max-width: 770px) {
132
+ .left-section .inner-content {
133
+ position: relative;
134
+ padding: 1rem 0;
135
+ }
136
+ }
137
+
138
+ .heading {
139
+ text-align: center;
140
+ font-size: 9em;
141
+ line-height: 1.3em;
142
+ margin: 2rem 0 0.5rem 0;
143
+ padding: 0;
144
+ text-shadow: 0 0 1rem #fefefe;
145
+ }
146
+ @media (max-width: 770px) {
147
+ .heading {
148
+ font-size: 7em;
149
+ line-height: 1.15;
150
+ margin: 0;
151
+ }
152
+ }
153
+
154
+ .subheading {
155
+ text-align: center;
156
+ max-width: 480px;
157
+ font-size: 1.5em;
158
+ line-height: 1.15em;
159
+ padding: 0 1rem;
160
+ margin: 0 auto;
161
+ }
162
+ @media (max-width: 770px) {
163
+ .subheading {
164
+ font-size: 1.3em;
165
+ line-height: 1.15;
166
+ max-width: 100%;
167
+ }
168
+ }
169
+
170
+ .right-section {
171
+ width: 50%;
172
+ }
173
+ @media (max-width: 770px) {
174
+ .right-section {
175
+ width: 100%;
176
+ height: 60%;
177
+ position: absolute;
178
+ bottom: 0;
179
+ }
180
+ }
181
+
182
+ .svgimg {
183
+ position: absolute;
184
+ bottom: 0;
185
+ padding-top: 10vh;
186
+ padding-left: 1vh;
187
+ max-width: 100%;
188
+ max-height: 100%;
189
+ }
190
+ @media (max-width: 770px) {
191
+ .svgimg {
192
+ padding: 0;
193
+ }
194
+ }
195
+ .svgimg .bench-legs {
196
+ fill: #0C0E10;
197
+ }
198
+ .svgimg .top-bench, .svgimg .bottom-bench {
199
+ stroke: #0C0E10;
200
+ stroke-width: 1px;
201
+ fill: #5B3E2B;
202
+ }
203
+ .svgimg .bottom-bench path:nth-child(1) {
204
+ fill: #432d20;
205
+ }
206
+ .svgimg .lamp-details {
207
+ fill: #202425;
208
+ }
209
+ .svgimg .lamp-accent {
210
+ fill: #2c3133;
211
+ }
212
+ .svgimg .lamp-bottom {
213
+ fill: linear-gradient(#202425, #0C0E10);
214
+ }
215
+ .svgimg .lamp-light {
216
+ fill: #EFEFEF;
217
+ }
218
+
219
+ @keyframes glow {
220
+ 0% {
221
+ text-shadow: 0 0 1rem #fefefe;
222
+ }
223
+ 50% {
224
+ text-shadow: 0 0 1.85rem #ededed;
225
+ }
226
+ 100% {
227
+ text-shadow: 0 0 1rem #fefefe;
228
+ }
229
+ }
230
+
231
+ </style>
@@ -0,0 +1,231 @@
1
+ <div class="background">
2
+ <div class="ground"></div>
3
+ </div>
4
+ <div class="container">
5
+ <div class="left-section">
6
+ <div class="inner-content">
7
+ <h1 class="heading">500</h1>
8
+ <p class="subheading">Looks like the page you were looking for is no longer here.</p>
9
+ </div>
10
+ </div>
11
+ <div class="right-section">
12
+ <svg class="svgimg" xmlns="http://www.w3.org/2000/svg" viewBox="51.5 -15.288 385 505.565">
13
+ <g class="bench-legs">
14
+ <path d="M202.778,391.666h11.111v98.611h-11.111V391.666z M370.833,390.277h11.111v100h-11.111V390.277z M183.333,456.944h11.111
15
+ v33.333h-11.111V456.944z M393.056,456.944h11.111v33.333h-11.111V456.944z" />
16
+ </g>
17
+ <g class="top-bench">
18
+ <path d="M396.527,397.917c0,1.534-1.243,2.777-2.777,2.777H190.972c-1.534,0-2.778-1.243-2.778-2.777v-8.333
19
+ c0-1.535,1.244-2.778,2.778-2.778H393.75c1.534,0,2.777,1.243,2.777,2.778V397.917z M400.694,414.583
20
+ c0,1.534-1.243,2.778-2.777,2.778H188.194c-1.534,0-2.778-1.244-2.778-2.778v-8.333c0-1.534,1.244-2.777,2.778-2.777h209.723
21
+ c1.534,0,2.777,1.243,2.777,2.777V414.583z M403.473,431.25c0,1.534-1.244,2.777-2.778,2.777H184.028
22
+ c-1.534,0-2.778-1.243-2.778-2.777v-8.333c0-1.534,1.244-2.778,2.778-2.778h216.667c1.534,0,2.778,1.244,2.778,2.778V431.25z"
23
+ />
24
+ </g>
25
+ <g class="bottom-bench">
26
+ <path d="M417.361,459.027c0,0.769-1.244,1.39-2.778,1.39H170.139c-1.533,0-2.777-0.621-2.777-1.39v-4.86
27
+ c0-0.769,1.244-0.694,2.777-0.694h244.444c1.534,0,2.778-0.074,2.778,0.694V459.027z" />
28
+ <path d="M185.417,443.75H400c0,0,18.143,9.721,17.361,10.417l-250-0.696C167.303,451.65,185.417,443.75,185.417,443.75z" />
29
+ </g>
30
+ <g id="lamp">
31
+ <path class="lamp-details" d="M125.694,421.997c0,1.257-0.73,3.697-1.633,3.697H113.44c-0.903,0-1.633-2.44-1.633-3.697V84.917
32
+ c0-1.257,0.73-2.278,1.633-2.278h10.621c0.903,0,1.633,1.02,1.633,2.278V421.997z"
33
+ />
34
+ <path class="lamp-accent" d="M128.472,93.75c0,1.534-1.244,2.778-2.778,2.778h-13.889c-1.534,0-2.778-1.244-2.778-2.778V79.861
35
+ c0-1.534,1.244-2.778,2.778-2.778h13.889c1.534,0,2.778,1.244,2.778,2.778V93.75z" />
36
+
37
+ <circle class="lamp-light" cx="119.676" cy="44.22" r="40.51" />
38
+ <path class="lamp-details" d="M149.306,71.528c0,3.242-13.37,13.889-29.861,13.889S89.583,75.232,89.583,71.528c0-4.166,13.369-13.889,29.861-13.889
39
+ S149.306,67.362,149.306,71.528z"/>
40
+ <radialGradient class="light-gradient" id="SVGID_1_" cx="119.676" cy="44.22" r="65" gradientUnits="userSpaceOnUse">
41
+ <stop offset="0%" style="stop-color:#FFFFFF; stop-opacity: 1"/>
42
+ <stop offset="50%" style="stop-color:#EDEDED; stop-opacity: 0.5">
43
+ <animate attributeName="stop-opacity" values="0.0; 0.5; 0.0" dur="5000ms" repeatCount="indefinite"></animate>
44
+ </stop>
45
+ <stop offset="100%" style="stop-color:#EDEDED; stop-opacity: 0"/>
46
+ </radialGradient>
47
+ <circle class="lamp-light__glow" fill="url(#SVGID_1_)" cx="119.676" cy="44.22" r="65"/>
48
+ <path class="lamp-bottom" d="M135.417,487.781c0,1.378-1.244,2.496-2.778,2.496H106.25c-1.534,0-2.778-1.118-2.778-2.496v-74.869
49
+ c0-1.378,1.244-2.495,2.778-2.495h26.389c1.534,0,2.778,1.117,2.778,2.495V487.781z" />
50
+ </g>
51
+ </svg>
52
+ </div>
53
+ </div>
54
+
55
+ <style>
56
+ @import url("https://fonts.googleapis.com/css?family=Fira+Sans");
57
+ /*Variables */
58
+ .left-section .inner-content {
59
+ position: absolute;
60
+ top: 50%;
61
+ transform: translateY(-50%);
62
+ }
63
+
64
+ * {
65
+ box-sizing: border-box;
66
+ }
67
+
68
+ html, body {
69
+ margin: 0;
70
+ padding: 0;
71
+ }
72
+
73
+ body {
74
+ font-family: "Fira Sans", sans-serif;
75
+ color: #f5f6fa;
76
+ }
77
+
78
+ .background {
79
+ position: absolute;
80
+ top: 0;
81
+ left: 0;
82
+ width: 100%;
83
+ height: 100%;
84
+ background: linear-gradient(#0C0E10, #446182);
85
+ }
86
+ .background .ground {
87
+ position: absolute;
88
+ bottom: 0;
89
+ width: 100%;
90
+ height: 25vh;
91
+ background: #0C0E10;
92
+ }
93
+ @media (max-width: 770px) {
94
+ .background .ground {
95
+ height: 0vh;
96
+ }
97
+ }
98
+
99
+ .container {
100
+ position: relative;
101
+ margin: 0 auto;
102
+ width: 85%;
103
+ height: 100vh;
104
+ padding-bottom: 25vh;
105
+ display: flex;
106
+ flex-direction: row;
107
+ justify-content: space-around;
108
+ }
109
+ @media (max-width: 770px) {
110
+ .container {
111
+ flex-direction: column;
112
+ padding-bottom: 0vh;
113
+ }
114
+ }
115
+
116
+ .left-section, .right-section {
117
+ position: relative;
118
+ }
119
+
120
+ .left-section {
121
+ width: 40%;
122
+ }
123
+ @media (max-width: 770px) {
124
+ .left-section {
125
+ width: 100%;
126
+ height: 40%;
127
+ position: absolute;
128
+ top: 0;
129
+ }
130
+ }
131
+ @media (max-width: 770px) {
132
+ .left-section .inner-content {
133
+ position: relative;
134
+ padding: 1rem 0;
135
+ }
136
+ }
137
+
138
+ .heading {
139
+ text-align: center;
140
+ font-size: 9em;
141
+ line-height: 1.3em;
142
+ margin: 2rem 0 0.5rem 0;
143
+ padding: 0;
144
+ text-shadow: 0 0 1rem #fefefe;
145
+ }
146
+ @media (max-width: 770px) {
147
+ .heading {
148
+ font-size: 7em;
149
+ line-height: 1.15;
150
+ margin: 0;
151
+ }
152
+ }
153
+
154
+ .subheading {
155
+ text-align: center;
156
+ max-width: 480px;
157
+ font-size: 1.5em;
158
+ line-height: 1.15em;
159
+ padding: 0 1rem;
160
+ margin: 0 auto;
161
+ }
162
+ @media (max-width: 770px) {
163
+ .subheading {
164
+ font-size: 1.3em;
165
+ line-height: 1.15;
166
+ max-width: 100%;
167
+ }
168
+ }
169
+
170
+ .right-section {
171
+ width: 50%;
172
+ }
173
+ @media (max-width: 770px) {
174
+ .right-section {
175
+ width: 100%;
176
+ height: 60%;
177
+ position: absolute;
178
+ bottom: 0;
179
+ }
180
+ }
181
+
182
+ .svgimg {
183
+ position: absolute;
184
+ bottom: 0;
185
+ padding-top: 10vh;
186
+ padding-left: 1vh;
187
+ max-width: 100%;
188
+ max-height: 100%;
189
+ }
190
+ @media (max-width: 770px) {
191
+ .svgimg {
192
+ padding: 0;
193
+ }
194
+ }
195
+ .svgimg .bench-legs {
196
+ fill: #0C0E10;
197
+ }
198
+ .svgimg .top-bench, .svgimg .bottom-bench {
199
+ stroke: #0C0E10;
200
+ stroke-width: 1px;
201
+ fill: #5B3E2B;
202
+ }
203
+ .svgimg .bottom-bench path:nth-child(1) {
204
+ fill: #432d20;
205
+ }
206
+ .svgimg .lamp-details {
207
+ fill: #202425;
208
+ }
209
+ .svgimg .lamp-accent {
210
+ fill: #2c3133;
211
+ }
212
+ .svgimg .lamp-bottom {
213
+ fill: linear-gradient(#202425, #0C0E10);
214
+ }
215
+ .svgimg .lamp-light {
216
+ fill: #EFEFEF;
217
+ }
218
+
219
+ @keyframes glow {
220
+ 0% {
221
+ text-shadow: 0 0 1rem #fefefe;
222
+ }
223
+ 50% {
224
+ text-shadow: 0 0 1.85rem #ededed;
225
+ }
226
+ 100% {
227
+ text-shadow: 0 0 1rem #fefefe;
228
+ }
229
+ }
230
+
231
+ </style>
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ErrorsController < ApplicationController
4
+ def not_found
5
+ render(status: 404)
6
+ end
7
+
8
+ def internal_server_error
9
+ render(status: 500)
10
+ end
11
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/migration'
4
+
5
+ module SeoOptimizer
6
+ class ModelsGenerator < Rails::Generators::NamedBase
7
+ source_root File.expand_path('templates', __dir__)
8
+ include Rails::Generators::Migration
9
+ desc 'Generates migration for models'
10
+
11
+ argument :desired_fields, type: :array, default: [], banner: "field field"
12
+
13
+ def self.orm
14
+ Rails::Generators.options[:rails][:orm]
15
+ end
16
+
17
+ def self.orm_has_migration?
18
+ [:active_record].include? orm
19
+ end
20
+
21
+ def self.next_migration_number(_path)
22
+ Time.now.utc.strftime('%Y%m%d%H%M%S')
23
+ end
24
+
25
+ def create_migration_file
26
+ return unless self.class.orm_has_migration?
27
+
28
+ migration_template 'model_template.erb',
29
+ "db/migrate/add_#{model_name}_seo_slug_migration.rb",
30
+ migration_version: migration_version,
31
+ model_name: model_name,
32
+ model_table: model_table,
33
+ desired_fields_reference: desired_fields_reference
34
+
35
+ end
36
+
37
+ private
38
+
39
+ def model
40
+ name
41
+ end
42
+
43
+ def model_name
44
+ model.downcase
45
+ end
46
+
47
+ def model_table
48
+ model_name.pluralize(2)
49
+ end
50
+
51
+ def desired_fields_reference
52
+ desired_fields.delete('seo_slug')
53
+ desired_fields.join(', ')
54
+ end
55
+
56
+ def migration_version
57
+ if rails5?
58
+ '[4.2]'
59
+ elsif rails6?
60
+ '[6.0]'
61
+ end
62
+ end
63
+
64
+ def rails5?
65
+ Rails.version.start_with? '5'
66
+ end
67
+
68
+ def rails6?
69
+ Rails.version.start_with? '6'
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,19 @@
1
+ class Add<%= model_name.capitalize %>SeoSlugMigration < ActiveRecord::Migration<%= migration_version %>
2
+ def up
3
+ add_column(:<%= model_table %>, :seo_slug, :string) unless column_exists?(:<%= model_table %>, :seo_slug)
4
+ ActiveRecord::Base.connection.execute(query)
5
+ end
6
+
7
+ def down
8
+ remove_column :<%= model_table %>, :seo_slug
9
+ end
10
+
11
+ private
12
+
13
+ def query
14
+ <<-SQL
15
+ UPDATE <%= model_table %>
16
+ SET seo_slug = concat_ws('-', id, <%= desired_fields_reference %>)
17
+ SQL
18
+ end
19
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/migration'
4
+
5
+ module SeoOptimizer
6
+ class RobotsFilesGenerator < Rails::Generators::Base
7
+ source_root File.expand_path('templates', __dir__)
8
+ desc 'Generates robots files'
9
+
10
+ def create_controller_files
11
+ prod_variable = Rails.configuration.sitemap_config_variable rescue raise(SeoOptimizer::Error, setup_error_message)
12
+ url = prod_variable[:production_url]
13
+ raise SeoOptimizer::Error, replace_url_error if !url || url == 'https://YOUR-PRODUCTION-URL.com'
14
+
15
+ site_map_xml
16
+ robots_text
17
+ end
18
+
19
+ private
20
+
21
+ def robots_text
22
+ routing_code = %(get 'robots.:format', to: 'robots#show')
23
+ route routing_code
24
+ %x(rm -f public/robots.txt)
25
+ template 'robots_controller_template.erb', File.join('app/controllers', 'robots_controller.rb')
26
+ %x(mkdir -p app/views/robots)
27
+ template 'robots_show_template.erb', File.join('app/views/robots', 'show.txt.erb')
28
+ end
29
+
30
+ def site_map_xml
31
+ routing_code = %(get 'sitemap.xml', to: 'sitemaps#show', as: :sitemap, defaults: { format: :xml })
32
+ route routing_code
33
+ template 'sitemaps_controller_template.erb', File.join('app/controllers', 'sitemaps_controller.rb')
34
+ template 'sitemaps_task_template.erb', File.join('lib/tasks/sitemap', 'generate.rake')
35
+ end
36
+
37
+ def replace_url_error
38
+ '
39
+ Please replace https://YOUR-PRODUCTION-URL.com by your production url in application.rb,
40
+ check the doc https://github.com/RonanLOUARN/seo_optimizer for more infos.
41
+ '
42
+ end
43
+
44
+ def setup_error_message
45
+ '
46
+ Please type "rails generate seo_optimizer:setups" in terminal,
47
+ check the doc https://github.com/RonanLOUARN/seo_optimizer for more infos.
48
+ '
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RobotsController < ApplicationController
4
+ def show
5
+ respond_to do |format|
6
+ format.text do
7
+ render 'robots/show.txt.erb'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ User-Agent: *
2
+ Disallow: /admin
3
+ Sitemap: <%= Rails.configuration.sitemap_config_variable[:production_url] %>/sitemap.xml
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SitemapsController < ApplicationController
4
+ def show
5
+ file = Zlib::GzipReader.new(open('public/sitemap.xml.gz'))
6
+ render xml: file.read
7
+ end
8
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :sitemap do
4
+ task generate: :environment do
5
+ SitemapGenerator::Sitemap.default_host = '<%= Rails.configuration.sitemap_config_variable[:production_url] %>'
6
+ SitemapGenerator::Sitemap.create_index = false
7
+ SitemapGenerator::Sitemap.create do
8
+ routes = Rails.application.routes.routes.map do |route|
9
+ {
10
+ alias: route.name,
11
+ path: route.path.spec.to_s,
12
+ controller: route.defaults[:controller],
13
+ action: route.defaults[:action],
14
+ }
15
+ end
16
+
17
+ # Set a list of controllers you don't want to generate routes for.
18
+ # /rails/info in particular maps to something inaccessible.
19
+ # redirects have a nil controller. This prevents duplicate content penalties.
20
+ banned_controllers = ["rails/info", nil]
21
+ routes.reject! { |route| banned_controllers.include?(route[:controller]) }
22
+
23
+ # sitemap_generator includes root by default; prevent duplication
24
+ routes.reject! { |route| route[:path] == '/' }
25
+
26
+ routes.each { |route| add route[:path][0..-11] } # Strips off '(.:format)
27
+
28
+ # Notice the below if you're hosting Jekyll/Octopress in a subdirectory
29
+ # or otherwise want to index content outside of Rails' routes.
30
+ # add_to_index '/sitemaps/show.xml'
31
+
32
+ # example for user resources
33
+ # User.select(:seo_slug, :updated_at).each do |slug|
34
+ # add user_path(slug), lastmod: slug.updated_at
35
+ # end
36
+ end
37
+ SitemapGenerator::Sitemap.ping_search_engines('<%= Rails.configuration.sitemap_config_variable[:production_url] %>/sitemap.xml')
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/migration'
4
+
5
+ module SeoOptimizer
6
+ class SetupsGenerator < Rails::Generators::Base
7
+ source_root File.expand_path('templates', __dir__)
8
+ desc 'Generates setup lines'
9
+
10
+ def create_controller_files
11
+ Rails.configuration.sitemap_config_variable
12
+ rescue
13
+ inject_into_file 'config/application.rb', :before => " end" do
14
+ "\n config.sitemap_config_variable = {}" +
15
+ "\n config.sitemap_config_variable[:production_url] = 'https://YOUR-PRODUCTION-URL.com'\n\n"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'seo_optimizer/version'
4
+ require 'sitemap_generator'
5
+
6
+ class SeoOptimizer::Error < StandardError; end
@@ -0,0 +1,3 @@
1
+ module SeoOptimizer
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require_relative 'lib/seo_optimizer/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = "seo_optimizer"
10
+ spec.version = SeoOptimizer::VERSION
11
+ spec.authors = ["Ronan Louarn"]
12
+ spec.email = ["ronan33720@hotmail.com"]
13
+
14
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
15
+ spec.summary = %q{Simple gem to improve SEO to your rails app.}
16
+ spec.description = %q{Simple gem to improve SEO to your rails app.}
17
+ spec.homepage = "https://www.lr-dev.fr"
18
+ spec.license = "MIT"
19
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
20
+
21
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
22
+
23
+ spec.metadata["homepage_uri"] = spec.homepage
24
+ spec.metadata["source_code_uri"] = "https://github.com/RonanLOUARN/seo_optimizer"
25
+ spec.metadata["changelog_uri"] = "https://github.com/RonanLOUARN/seo_optimizer/commits/master"
26
+
27
+ # Specify which files should be added to the gem when it is released.
28
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
29
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
30
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
31
+ end
32
+ spec.bindir = "exe"
33
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
34
+ spec.require_paths = ["lib"]
35
+ spec.add_dependency 'sitemap_generator'
36
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: seo_optimizer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ronan Louarn
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-11-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sitemap_generator
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Simple gem to improve SEO to your rails app.
28
+ email:
29
+ - ronan33720@hotmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - ".rspec"
36
+ - ".ruby-version"
37
+ - ".travis.yml"
38
+ - CODE_OF_CONDUCT.md
39
+ - Gemfile
40
+ - Gemfile.lock
41
+ - LICENSE.txt
42
+ - README.md
43
+ - Rakefile
44
+ - bin/console
45
+ - bin/setup
46
+ - lib/generators/seo_optimizer/errors_pages/errors_pages_generator.rb
47
+ - lib/generators/seo_optimizer/errors_pages/templates/404.erb
48
+ - lib/generators/seo_optimizer/errors_pages/templates/500.erb
49
+ - lib/generators/seo_optimizer/errors_pages/templates/errors_controller_template.erb
50
+ - lib/generators/seo_optimizer/models/models_generator.rb
51
+ - lib/generators/seo_optimizer/models/templates/model_template.erb
52
+ - lib/generators/seo_optimizer/robots_files/robots_files_generator.rb
53
+ - lib/generators/seo_optimizer/robots_files/templates/robots_controller_template.erb
54
+ - lib/generators/seo_optimizer/robots_files/templates/robots_show_template.erb
55
+ - lib/generators/seo_optimizer/robots_files/templates/sitemaps_controller_template.erb
56
+ - lib/generators/seo_optimizer/robots_files/templates/sitemaps_task_template.erb
57
+ - lib/generators/seo_optimizer/setups/setups_generator.rb
58
+ - lib/seo_optimizer.rb
59
+ - lib/seo_optimizer/version.rb
60
+ - seo_optimizer.gemspec
61
+ homepage: https://www.lr-dev.fr
62
+ licenses:
63
+ - MIT
64
+ metadata:
65
+ allowed_push_host: https://rubygems.org
66
+ homepage_uri: https://www.lr-dev.fr
67
+ source_code_uri: https://github.com/RonanLOUARN/seo_optimizer
68
+ changelog_uri: https://github.com/RonanLOUARN/seo_optimizer/commits/master
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 2.3.0
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubygems_version: 3.1.2
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Simple gem to improve SEO to your rails app.
88
+ test_files: []