rails_friendly_urls 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6e2ea43784b8044cc81c72711d18ca5a4c7b6bf9
4
+ data.tar.gz: 5d7e8cb96eb2d0b28abd5669bc1283e55bc4e471
5
+ SHA512:
6
+ metadata.gz: e8291e4dbdf95a576394897f5a73a5d946a41581ab8fcd184cd6c652d7de324b53edaa5b9ae332a29e52f9fd52fa8eeed00ce6dc498ef72b9a0b9ac18b2b627b
7
+ data.tar.gz: 74465748d887ca85fdbf616b21b418b61d3f0b4a3106e2ba5c0b5299c3e2d3b08734dafd8ab3aa8efcaf555672fe6ca7d738494bb7f79c00cd6a954758a4d95f
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Carlos Alonso Pérez
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ #Rails Friendly Urls Engine
2
+ [![Build Status](https://travis-ci.org/calonso/rails_friendly_urls.svg?branch=master)](https://travis-ci.org/calonso/rails_friendly_urls) [![Code Climate](https://codeclimate.com/github/calonso/rails_friendly_urls/badges/gpa.svg)](https://codeclimate.com/github/calonso/rails_friendly_urls) [![Test Coverage](https://codeclimate.com/github/calonso/rails_friendly_urls/badges/coverage.svg)](https://codeclimate.com/github/calonso/rails_friendly_urls/coverage) [![calonso/rails_friendly_urls API Documentation](https://www.omniref.com/github/calonso/rails_friendly_urls.png)](https://www.omniref.com/github/calonso/rails_friendly_urls)
3
+
4
+ Rails Gem to easily configure any url as a friendlier one.
5
+
6
+ ##Features
7
+
8
+ * Allows customisation of **ABSOLUTELY** any url into a SEO friendlier one.
9
+ * Takes care of the parameters you defined in your original route and will pass them into the controller when the friendly url is invoked.
10
+ * Takes care of named routes and both url and path helpers so that there's no need to change a single line of code when adding a new friendly url.
11
+ * When a friendly URL is defined to substitute another one. The non-friendly one is automatically configured to redirect to the friendly path so that you're not penalised by search engines.
12
+ * Doesn't force any particular storage for the friendly url's data.
13
+
14
+ ##Example application
15
+
16
+ You can see the gem running live in the following url: [https://rails-friendly-urls-test.herokuapp.com/](https://rails-friendly-urls-test.herokuapp.com/)
17
+
18
+ The source code for this example project is available here: [https://github.com/calonso/rails_friendly_urls_test](https://github.com/calonso/rails_friendly_urls_test)
19
+
20
+ ##Installation
21
+
22
+ Installing this gem only requires you to add the following line to your `Gemfile`
23
+
24
+ ```ruby
25
+ gem 'rails_friendly_urls'
26
+ ```
27
+
28
+ Run
29
+
30
+ ```
31
+ $ bundle install
32
+ $ rails generate rails_friendly_urls:install
33
+ ```
34
+
35
+ ##Setup
36
+
37
+ Here I detail you the steps that I followed to set up the friendly urls engine in the example application, so most of them should be the same for you, some others slightly different, but don't worry, you'll see appropriated explanations while reading this steps.
38
+
39
+ ###1. Friendly URLs Storage
40
+
41
+ First of all we need to decide our urls storage technology, in my case I decided to use a standard activerecord rails model, but I guess that a YAML file could do the job as well or any other persistence technology.
42
+
43
+ 5 fields are required to be stored to be able to build a friendly url. They are:
44
+
45
+ 1. Path
46
+ 2. Slug
47
+ 3. Controller
48
+ 4. Action
49
+ 5. Defaults
50
+
51
+ Just a reminder that you must make your storage engine to manage the defaults field as a hash.
52
+
53
+ Once you've done it, remember to include the `RailsFriendlyUrls::FriendlyUrl` module in that class so that you can use `set_destination_data!` method. That method will complete all your `controller`, `action` and `defaults` fields once you've provided `path` and `slug`.
54
+
55
+ ###2. The Manager
56
+
57
+ After running the bundled installer (`$ rails generate rails_friendly_urls:install`) a new but incomplete file appears at `config/initializers/friendly_urls_manager.rb`. We need to complete the `urls` method in that file to make it return the list of friendly url objects (objects that simply respond to the five methods described above, i.e: path, slug, controller, action and defaults)
58
+
59
+ In my example project this is the final implementation:
60
+
61
+ ```
62
+ # FriendlyUrls Manager contents
63
+ class RailsFriendlyUrls::Manager
64
+ def self.urls
65
+ ::FriendlyUrl.all
66
+ end
67
+ end
68
+ ```
69
+
70
+ Simple, huh?
71
+
72
+ ###3. URL injection
73
+
74
+ As part of the installation process, a new line is inserted on top of the `routes.rb` file that simply invokes the friendly urls engine to do its magic.
75
+
76
+ ##Caveats
77
+
78
+ * At the moment, only GET requests are supported.
79
+ * At the moment, the rails application has to be restarted for the new urls to start working.
80
+
81
+ ## Contributing
82
+
83
+ 1. Fork it ( https://github.com/calonso/rails_friendly_urls/fork )
84
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
85
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
86
+ 4. Push to the branch (`git push origin my-new-feature`)
87
+ 5. Create a new Pull Request
88
+
89
+ Released under the MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require 'bundler'
4
+ require 'appraisal'
5
+
6
+ require 'rspec/core/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task :default => :spec
11
+
12
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
13
+ task :default => :appraisal
14
+ end
@@ -0,0 +1,19 @@
1
+ require 'rails/generators'
2
+
3
+ class RailsFriendlyUrls::InstallGenerator < Rails::Generators::Base
4
+ desc 'Creates a boilerplate Friendly Urls Manager with an empty implementation for you to complete.'
5
+ def create_friendly_urls_manager
6
+ create_file "config/initializers/friendly_urls_manager.rb", <<-EOS
7
+ # FriendlyUrls Manager contents
8
+ class RailsFriendlyUrls::Manager
9
+ def self.urls
10
+ raise NotImplementedError.new 'RailsFriendlyUrls::Manager::urls not implemented at config/initializers/friendly_urls_manager.rb'
11
+ end
12
+ end
13
+ EOS
14
+ end
15
+
16
+ def inject_urls
17
+ insert_into_file "config/routes.rb", "\n RailsFriendlyUrls::Manager.inject_urls self\n", :after => ".routes.draw do\n"
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ require 'singleton'
2
+ require 'rails'
3
+
4
+ #
5
+ # Base gem's module
6
+ #
7
+ # @author Carlos Alonso
8
+ #
9
+ module RailsFriendlyUrls
10
+
11
+ #
12
+ # Method to quickly get the major and minor versions of the current Rails env
13
+ #
14
+ # @returns [String] with X.Y format with major and minor versions
15
+ def self.rails_version
16
+ "#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}"
17
+ end
18
+
19
+ end
20
+
21
+ require 'rails_friendly_urls/manager'
22
+ require 'rails_friendly_urls/friendly_url'
23
+ require 'rails_friendly_urls/route_sets/route_set'
24
+
25
+ case RailsFriendlyUrls.rails_version
26
+ when '4.2'
27
+ require 'rails_friendly_urls/urls/rails4_2'
28
+ when '4.0', '4.1'
29
+ require 'rails_friendly_urls/urls/rails4_0'
30
+ when '3.2'
31
+ require 'rails_friendly_urls/route_sets/rails3'
32
+ else
33
+ raise NotImplementedError.new "Rails Friendly URLs gem doesn't support Rails #{Rails.version}"
34
+ end
@@ -0,0 +1,21 @@
1
+ module RailsFriendlyUrls
2
+ #
3
+ # This module is to be included in the client class that represents the Friendly URL
4
+ #
5
+ # @author Carlos Alonso
6
+ #
7
+ module FriendlyUrl
8
+ #
9
+ # This method tries to identify the route contained at self.path to extract
10
+ # the destination's controller, action and other arguments and save them
11
+ # into the corresponding controller, action and defaults fields of the
12
+ # including objects.
13
+ #
14
+ def set_destination_data!
15
+ route_info = Rails.application.routes.recognize_path self.path
16
+ self.controller = route_info[:controller]
17
+ self.action = route_info[:action]
18
+ self.defaults = route_info.reject { |k, v| [:controller, :action].include? k }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,55 @@
1
+ module RailsFriendlyUrls
2
+ #
3
+ # This class is responsible for orchestrating the whole gem's functions.
4
+ #
5
+ # @author Carlos Alonso
6
+ #
7
+ class Manager
8
+ # It is designed to be a Singleton, only one instance will run per process.
9
+ include Singleton
10
+
11
+ #
12
+ # Injects the defined urls into the given RouteSet. This method should be invoked
13
+ # first thing in the routes.rb block
14
+ #
15
+ # @param [ActionDispatch::Routing::RouteSet] rails' current RouteSet.
16
+ def self.inject_urls(mapper)
17
+ list = self.urls || []
18
+ Rails.logger.warn "Injecting empty Friendly URLs List!!" if list.empty?
19
+ list.each do |f_url|
20
+ mapper.get f_url.slug, to: "#{f_url.controller}##{f_url.action}", defaults: f_url.defaults
21
+ mapper.get f_url.path, to: mapper.redirect(f_url.slug)
22
+ end
23
+ end
24
+
25
+ #
26
+ # Invokes Rails' routes reload. Useful to reload the routes after
27
+ # modifying the list of SEO Friendly defined ones without having to
28
+ # restart the application server.
29
+ #
30
+ def self.apply_changes!
31
+ Rails.application.reload_routes!
32
+ end
33
+
34
+ #
35
+ # This method is used in Rails' URL Helpers to return the corresponding
36
+ # SEO Friendly URL instead of the default one.
37
+ #
38
+ # @param [String] The path we want the URL for.
39
+ # @returns [String] The SEO Friendly Slug for the required path.
40
+ def self.url_for(path)
41
+ self.instance.friendly path
42
+ end
43
+
44
+ #
45
+ # INTERNAL: Searches the list of SEO Friendly defined substitutions for
46
+ # the corresponding to the given path
47
+ #
48
+ # @param [String] The path we want the substitution for.
49
+ # @returns [String] The SEO Friendly Slugh for the required path.
50
+ def friendly(path)
51
+ @all_friendly ||= Hash[*RailsFriendlyUrls::Manager.urls.map { |f_url| [f_url.path, f_url.slug] }.flatten]
52
+ @all_friendly[path] || path
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,45 @@
1
+ module ActionDispatch
2
+ module Routing
3
+ #
4
+ # Monkey Patched Rails' class ActionDispatch::Routing::RouteSet.
5
+ #
6
+ # @author Carlos Alonso
7
+ #
8
+ class RouteSet
9
+ #
10
+ # Monkey Patched Rails' method: Includes a call to RailsFriendlyUrls::Manager.url_for
11
+ # when the Rails' URL Helper is building a url for a path to use the
12
+ # configured SEO Friendly substutition if any.
13
+ #
14
+ def url_for(options = {})
15
+ finalize!
16
+ options = (options || {}).reverse_merge!(default_url_options)
17
+
18
+ handle_positional_args(options)
19
+
20
+ user, password = extract_authentication(options)
21
+ path_segments = options.delete(:_path_segments)
22
+ script_name = options.delete(:script_name)
23
+
24
+ path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
25
+
26
+ path_options = options.except(*RESERVED_OPTIONS)
27
+ path_options = yield(path_options) if block_given?
28
+
29
+ path_addition, params = generate(path_options, path_segments || {})
30
+ path << path_addition
31
+ params.merge!(options[:params] || {})
32
+
33
+ path = RailsFriendlyUrls::Manager.url_for path
34
+
35
+ ActionDispatch::Http::URL.url_for(options.merge!({
36
+ :path => path,
37
+ :params => params,
38
+ :user => user,
39
+ :password => password
40
+ }))
41
+ end
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,97 @@
1
+ module ActionDispatch
2
+ module Routing
3
+ #
4
+ # Monkey Patched Rails' class ActionDispatch::Routing::RouteSet.
5
+ #
6
+ # @author Carlos Alonso
7
+ #
8
+ class RouteSet
9
+ #
10
+ # Monkey Patched Rails' method to recognize redirections as well as, for some
11
+ # reason, the original Rails' method doesn't.
12
+ #
13
+ def recognize_path(path, environment = {})
14
+ method = (environment[:method] || "GET").to_s.upcase
15
+ path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://}
16
+ extras = environment[:extras] || {}
17
+
18
+ begin
19
+ env = Rack::MockRequest.env_for(path, {:method => method})
20
+ rescue URI::InvalidURIError => e
21
+ raise ActionController::RoutingError, e.message
22
+ end
23
+
24
+ req = @request_class.new(env)
25
+ @router.recognize(req) do |route, _matches, params|
26
+ params = _matches if params.nil?
27
+ params.merge!(extras)
28
+ params.merge!(req.parameters.symbolize_keys)
29
+ params.each do |key, value|
30
+ if value.is_a?(String)
31
+ value = value.dup.force_encoding(Encoding::BINARY)
32
+ params[key] = URI.parser.unescape(value)
33
+ end
34
+ end
35
+
36
+ old_params = env[params_key]
37
+ env[params_key] = (old_params || {}).merge(params)
38
+ dispatcher = route.app
39
+ while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
40
+ dispatcher = dispatcher.app
41
+ end
42
+
43
+ if dispatcher.is_a?(Dispatcher)
44
+ if dispatcher.controller(params, false)
45
+ dispatcher.prepare_params!(params)
46
+ return params
47
+ else
48
+ raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
49
+ end
50
+ elsif dispatcher.is_a?(redirect_class)
51
+ return { status: 301, path: path_from_dispatcher(dispatcher) }
52
+ end
53
+ end
54
+
55
+ raise ActionController::RoutingError, "No route matches #{path.inspect}"
56
+ end
57
+
58
+ private
59
+
60
+ #
61
+ # INTERNAL: Helps deciding which module take the PARAMETERS_KEY constant
62
+ # from. This constant was moved in Rails 4.2 from one to another and
63
+ # using this method here allows us to reuse this file for all Rails 4.x
64
+ #
65
+ def params_key
66
+ defined?(::ActionDispatch::Http::Parameters::PARAMETERS_KEY) ?
67
+ ::ActionDispatch::Http::Parameters::PARAMETERS_KEY :
68
+ ::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY
69
+ end
70
+
71
+ #
72
+ # INTERNAL: Helps reusing code by deciding which class to consider
73
+ # as the redirection depending on the Rails version running.
74
+ #
75
+ # @returns [ActionDispatch::Routing::Redirect] or [ActionDispatch::Routing::PathRedirect]
76
+ def redirect_class
77
+ Rails::VERSION::MAJOR == 3 ? Redirect : PathRedirect
78
+ end
79
+
80
+ #
81
+ # INTERNAL: Helps reusing code by obtaining the path from the Rails'
82
+ # ActionDispatch::Routing::Dispatcher depending on the Rails version
83
+ # running.
84
+ #
85
+ # @param [ActionDispatch::Routing::Dispatcher] in use.
86
+ # @return [String] the destination path of the redirection.
87
+ def path_from_dispatcher(dispatcher)
88
+ if Rails::VERSION::MAJOR == 3
89
+ dispatcher.block.call({}, nil)
90
+ else
91
+ dispatcher.block
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+
@@ -0,0 +1,36 @@
1
+ module ActionDispatch
2
+ module Http
3
+ #
4
+ # Monkey Patched Rails' module ActionDispatch::Http::URL
5
+ #
6
+ # @author Carlos Alonso
7
+ #
8
+ module URL
9
+ #
10
+ # Monkey Patched Rails' method: Includes a call to RailsFriendlyUrls::Manager.url_for
11
+ # when the Rails' URL Helper is building a url for a path to use the
12
+ # configured SEO Friendly substutition if any.
13
+ #
14
+ def self.url_for(options = {})
15
+ options = options.dup
16
+ path = options.delete(:script_name).to_s.chomp("/")
17
+ path << options.delete(:path).to_s
18
+
19
+ add_trailing_slash(path) if options[:trailing_slash]
20
+
21
+ params = options[:params].is_a?(Hash) ? options[:params] : options.slice(:params)
22
+ params.reject! { |_,v| v.to_param.nil? }
23
+
24
+ result = build_host_url(options)
25
+
26
+ path = RailsFriendlyUrls::Manager.url_for path
27
+
28
+ result << path
29
+
30
+ result << "?#{params.to_query}" unless params.empty?
31
+ result << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
32
+ result
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ module ActionDispatch
2
+ module Http
3
+ #
4
+ # Monkey Patched Rails' module ActionDispatch::Http::URL
5
+ #
6
+ # @author Carlos Alonso
7
+ #
8
+ module URL
9
+ #
10
+ # Monkey Patched Rails' method: Includes a call to RailsFriendlyUrls::Manager.url_for
11
+ # when the Rails' URL Helper is building a url for a path to use the
12
+ # configured SEO Friendly substutition if any.
13
+ #
14
+ def self.path_for(options)
15
+ path = options[:script_name].to_s.chomp("/")
16
+ path << options[:path] if options.key?(:path)
17
+
18
+ add_trailing_slash(path) if options[:trailing_slash]
19
+ add_params(path, options[:params]) if options.key?(:params)
20
+ add_anchor(path, options[:anchor]) if options.key?(:anchor)
21
+
22
+ RailsFriendlyUrls::Manager.url_for path
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module RailsFriendlyUrls
2
+ VERSION = "0.1.2"
3
+ end
@@ -0,0 +1,46 @@
1
+ require "rails"
2
+ require 'rails/all'
3
+ require 'action_view/testing/resolvers'
4
+
5
+ require 'rails_friendly_urls' # our gem
6
+
7
+ class RailsFriendlyUrlsApp < Rails::Application
8
+ config.root = File.expand_path("../../..", __FILE__)
9
+ config.cache_classes = true
10
+
11
+ config.eager_load = false
12
+ config.serve_static_assets = true
13
+ config.static_cache_control = "public, max-age=3600"
14
+
15
+ config.consider_all_requests_local = true
16
+ config.action_controller.perform_caching = false
17
+
18
+ config.action_dispatch.show_exceptions = false
19
+
20
+ config.action_controller.allow_forgery_protection = false
21
+
22
+ config.active_support.deprecation = :stderr
23
+
24
+ config.middleware.delete "Rack::Lock"
25
+ config.middleware.delete "ActionDispatch::Flash"
26
+ config.middleware.delete "ActionDispatch::BestStandardsSupport"
27
+ config.secret_token = "49837489qkuweoiuoqwehisuakshdjksadhaisdy78o34y138974xyqp9rmye8yrpiokeuioqwzyoiuxftoyqiuxrhm3iou1hrzmjk"
28
+ routes.append do
29
+ get "/" => "welcome#index"
30
+ end
31
+ end
32
+
33
+ class ApplicationController < ActionController::Base
34
+ include Rails.application.routes.url_helpers
35
+ layout 'application'
36
+ self.view_paths = [ActionView::FixtureResolver.new(
37
+ "layouts/application.html.erb" => '<%= yield %>',
38
+ "welcome/index.html.erb"=> 'Hello from index.html.erb',
39
+ )]
40
+
41
+ def index
42
+ end
43
+
44
+ end
45
+
46
+ RailsFriendlyUrlsApp.initialize!
@@ -0,0 +1,47 @@
1
+ require "rails"
2
+ require 'rails/all'
3
+ require 'action_view/testing/resolvers'
4
+
5
+ require 'rails_friendly_urls' # our gem
6
+
7
+ module RailsFriendlyUrlsApp
8
+ class Application < Rails::Application
9
+ config.root = File.expand_path("../../..", __FILE__)
10
+ config.cache_classes = true
11
+
12
+ config.eager_load = false
13
+ config.static_cache_control = "public, max-age=3600"
14
+
15
+ config.consider_all_requests_local = true
16
+ config.action_controller.perform_caching = false
17
+
18
+ config.action_dispatch.show_exceptions = false
19
+
20
+ config.action_controller.allow_forgery_protection = false
21
+
22
+ config.active_support.deprecation = :stderr
23
+
24
+ config.middleware.delete "Rack::Lock"
25
+ config.middleware.delete "ActionDispatch::Flash"
26
+ config.middleware.delete "ActionDispatch::BestStandardsSupport"
27
+ config.secret_key_base = '49837489qkuweoiuoqwehisuakshdjksadhaisdy78o34y138974xyqp9rmye8yrpiokeuioqwzyoiuxftoyqiuxrhm3iou1hrzmjk'
28
+ routes.append do
29
+ get "/" => "application#index"
30
+ end
31
+ end
32
+ end
33
+
34
+ class ApplicationController < ActionController::Base
35
+ include Rails.application.routes.url_helpers
36
+ layout 'application'
37
+ self.view_paths = [ActionView::FixtureResolver.new(
38
+ "layouts/application.html.erb" => '<%= yield %>',
39
+ "welcome/index.html.erb"=> 'Hello from index.html.erb',
40
+ )]
41
+
42
+ def index
43
+ end
44
+
45
+ end
46
+
47
+ RailsFriendlyUrlsApp::Application.initialize!
@@ -0,0 +1,33 @@
1
+
2
+ require 'generators/rails_friendly_urls/install_generator'
3
+
4
+ describe RailsFriendlyUrls::InstallGenerator, type: :generator do
5
+ destination File.expand_path("../../tmp", __FILE__)
6
+
7
+ before do
8
+ prepare_destination
9
+ mkdir File.join(destination_root, 'config')
10
+ cp routes_file, File.join(destination_root, 'config/routes.rb')
11
+ run_generator
12
+ end
13
+
14
+ after do
15
+ rm_rf destination_root
16
+ end
17
+
18
+ it 'creates the Rails Friendly Urls Manager' do
19
+ assert_file "config/initializers/friendly_urls_manager.rb", <<-EOS
20
+ # FriendlyUrls Manager contents
21
+ class RailsFriendlyUrls::Manager
22
+ def self.urls
23
+ raise NotImplementedError.new 'RailsFriendlyUrls::Manager::urls not implemented at config/initializers/friendly_urls_manager.rb'
24
+ end
25
+ end
26
+ EOS
27
+ end
28
+
29
+ it 'injects the Rails Friendly Urls in routes' do
30
+ assert_file "config/routes.rb", routes_contents
31
+ end
32
+
33
+ end
@@ -0,0 +1,53 @@
1
+
2
+ describe RailsFriendlyUrls::FriendlyUrl do
3
+
4
+ describe '#set_destination_data' do
5
+
6
+ let(:path) { '/test' }
7
+ let(:controller) { 'application' }
8
+ let(:action) { 'index' }
9
+
10
+ before do
11
+ mapper = ActionDispatch::Routing::Mapper.new Rails.application.routes
12
+ mapper.get path, to: "#{controller}##{action}"
13
+ end
14
+
15
+ subject { DummyFriendlyURL.new path }
16
+
17
+ it 'successfully assigns controller' do
18
+ expect(subject.controller).to eq controller
19
+ end
20
+
21
+ it 'successfully assigns action' do
22
+ expect(subject.action).to eq action
23
+ end
24
+
25
+ describe 'defaults' do
26
+
27
+ context 'with no parameters' do
28
+ it 'works with no parameters' do
29
+ expect(subject.defaults).to eq({})
30
+ end
31
+ end
32
+
33
+ context 'using parameters in url' do
34
+
35
+ let(:path) { '/:lang/test' }
36
+
37
+ subject { DummyFriendlyURL.new '/es-ES/test' }
38
+
39
+ it 'successfully assigns defaults' do
40
+ expect(subject.defaults).to eq lang: 'es-ES'
41
+ end
42
+ end
43
+
44
+ context 'using parameters in query string' do
45
+ subject { DummyFriendlyURL.new '/test?lang=es-ES' }
46
+
47
+ it 'successfully assigns defaults' do
48
+ expect(subject.defaults).to eq lang: 'es-ES'
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,54 @@
1
+
2
+ shared_examples 'a successfully injected friendly url' do
3
+ let(:route_set) { Rails.application.routes }
4
+
5
+ before do
6
+ subject.urls = [url]
7
+ subject.inject_urls ActionDispatch::Routing::Mapper.new route_set
8
+ end
9
+
10
+ it 'successfully injects the url' do
11
+ route_info = Rails.application.routes.recognize_path url.slug
12
+ expect(route_info.delete(:controller)).to eq url.controller
13
+ expect(route_info.delete(:action)).to eq url.action
14
+ expect(route_info).to eq (url.defaults.reject { |k, v| [:controller, :action].include? k })
15
+ end
16
+
17
+ it 'adds the corresponding redirection route' do
18
+ route_info = Rails.application.routes.recognize_path url.path
19
+ expect(route_info[:status]).to eq 301
20
+ expect(route_info[:path]).to eq url.slug
21
+ end
22
+
23
+ it 'keeps path helpers working without changing code' do
24
+ expect(route_set.url_helpers.a_b_c_path).to eq url.slug
25
+ end
26
+
27
+ after do
28
+ route_set.clear!
29
+ end
30
+ end
31
+
32
+ describe RailsFriendlyUrls::Manager do
33
+
34
+ describe '#inject_urls' do
35
+
36
+ subject { RailsFriendlyUrls::Manager }
37
+
38
+ context 'with params in the url' do
39
+ let(:url) {
40
+ DummyFriendlyURL.new '/a/b/c', '/friendly1', 'application', 'acti', a:1, b:2
41
+ }
42
+
43
+ include_examples 'a successfully injected friendly url'
44
+ end
45
+
46
+ context 'with no params in the url' do
47
+ let(:url) {
48
+ DummyFriendlyURL.new '/a/b/c', '/friendly1', 'application', 'acti', {}
49
+ }
50
+
51
+ include_examples 'a successfully injected friendly url'
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,51 @@
1
+ # Configure Rails Environment
2
+ require "codeclimate-test-reporter"
3
+ CodeClimate::TestReporter.start
4
+
5
+ Bundler.setup
6
+
7
+ ENV["RAILS_ENV"] = "test"
8
+
9
+ require 'rails'
10
+
11
+ case "#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}"
12
+ when '3.2'
13
+ ENV['DATABASE_URL'] = 'sqlite3://localhost/:memory:'
14
+ require 'apps/rails3_2'
15
+ when '4.0'
16
+ ENV['DATABASE_URL'] = 'sqlite3://localhost/:memory:'
17
+ require 'apps/rails4'
18
+ when '4.1'
19
+ ENV['DATABASE_URL'] = 'sqlite3::memory:'
20
+ require 'apps/rails4'
21
+ when '4.2'
22
+ ENV['DATABASE_URL'] = 'sqlite3::memory:'
23
+ require 'apps/rails4'
24
+ else
25
+ raise NotImplementedError.new "Rails Friendly URLs gem doesn't support Rails #{Rails.version}"
26
+ end
27
+
28
+ Bundler.require :default
29
+ Bundler.require :development
30
+
31
+ require 'rspec/rails'
32
+ require 'rails_friendly_urls'
33
+
34
+ # Load support files
35
+ Dir["#{File.dirname(__FILE__)}/support/*.rb"].each { |f| require f }
36
+
37
+ RSpec.configure do |config|
38
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
39
+ #config.fixture_path = "#{::Rails.root}/spec/fixtures"
40
+
41
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
42
+ # examples within a transaction, remove the following line or assign false
43
+ # instead of true.
44
+ config.use_transactional_fixtures = true
45
+
46
+ # Run specs in random order to surface order dependencies. If you find an
47
+ # order dependency and want to debug it, you can fix the order by providing
48
+ # the seed, which is printed after each run.
49
+ # --seed 1234
50
+ config.order = :random
51
+ end
@@ -0,0 +1,19 @@
1
+
2
+ class DummyFriendlyURL
3
+
4
+ include RailsFriendlyUrls::FriendlyUrl
5
+
6
+ attr_accessor :path, :slug, :controller, :action, :defaults
7
+
8
+ def initialize(path, slug = nil, controller = nil, action = nil, defaults = nil)
9
+ @path = path
10
+ if slug
11
+ @slug = slug
12
+ @controller = controller
13
+ @action = action
14
+ @defaults = defaults
15
+ else
16
+ set_destination_data!
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+
2
+ class RailsFriendlyUrls::Manager
3
+
4
+ def self.urls=(urls)
5
+ @urls = urls
6
+ end
7
+
8
+ def self.urls
9
+ @urls
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ if Rails::VERSION::MAJOR == 4
2
+ require 'support/routes/rails4'
3
+ else
4
+ require 'support/routes/rails3'
5
+ end
6
+
7
+ def routes_file
8
+ if Rails::VERSION::MAJOR == 4
9
+ 'spec/support/routes/rails4.rb'
10
+ else
11
+ 'spec/support/routes/rails3.rb'
12
+ end
13
+ end
14
+
15
+ def routes_contents
16
+ if Rails::VERSION::MAJOR == 3
17
+ <<-EOS
18
+ RailsFriendlyUrlsApp.routes.draw do
19
+
20
+ RailsFriendlyUrls::Manager.inject_urls self
21
+
22
+ end
23
+ EOS
24
+ else
25
+ <<-EOS
26
+ RailsFriendlyUrlsApp::Application.routes.draw do
27
+
28
+ RailsFriendlyUrls::Manager.inject_urls self
29
+
30
+ end
31
+ EOS
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ RailsFriendlyUrlsApp.routes.draw do
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ RailsFriendlyUrlsApp::Application.routes.draw do
2
+
3
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_friendly_urls
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Carlos Alonso
8
+ - Maria Turnau
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-05-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '3.2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '3.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: sqlite3
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.3'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.3'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.2'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.2'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec-rails
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '3.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: generator_spec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '0.9'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '0.9'
84
+ description: Rails Gem to easily configure any url as a friendlier one.
85
+ email: info@mrcalonso.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - LICENSE
91
+ - README.md
92
+ - Rakefile
93
+ - lib/generators/rails_friendly_urls/install_generator.rb
94
+ - lib/rails_friendly_urls.rb
95
+ - lib/rails_friendly_urls/friendly_url.rb
96
+ - lib/rails_friendly_urls/manager.rb
97
+ - lib/rails_friendly_urls/route_sets/rails3.rb
98
+ - lib/rails_friendly_urls/route_sets/route_set.rb
99
+ - lib/rails_friendly_urls/urls/rails4_0.rb
100
+ - lib/rails_friendly_urls/urls/rails4_2.rb
101
+ - lib/rails_friendly_urls/version.rb
102
+ - spec/apps/rails3_2.rb
103
+ - spec/apps/rails4.rb
104
+ - spec/generators/install_generator_spec.rb
105
+ - spec/rails_friendly_urls/friendly_url_spec.rb
106
+ - spec/rails_friendly_urls/manager_spec.rb
107
+ - spec/spec_helper.rb
108
+ - spec/support/dummy_friendly_url.rb
109
+ - spec/support/dummy_manager_impl.rb
110
+ - spec/support/routes.rb
111
+ - spec/support/routes/rails3.rb
112
+ - spec/support/routes/rails4.rb
113
+ homepage: http://mrcalonso.com/rails-friendly-urls-gem/
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: 1.9.3
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.2.2
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: This is a Rails gem that allows to configure friendly urls for any url in
137
+ your project.
138
+ test_files:
139
+ - spec/apps/rails3_2.rb
140
+ - spec/apps/rails4.rb
141
+ - spec/generators/install_generator_spec.rb
142
+ - spec/rails_friendly_urls/friendly_url_spec.rb
143
+ - spec/rails_friendly_urls/manager_spec.rb
144
+ - spec/spec_helper.rb
145
+ - spec/support/dummy_friendly_url.rb
146
+ - spec/support/dummy_manager_impl.rb
147
+ - spec/support/routes/rails3.rb
148
+ - spec/support/routes/rails4.rb
149
+ - spec/support/routes.rb
150
+ has_rdoc: