oembed_proxy 0.1.0

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
+ SHA256:
3
+ metadata.gz: 956cb22dbe2035e94f2b5f3847559b595ff322f64d4fbf162a7b680c3b76c9e0
4
+ data.tar.gz: af361ad48515fb78f564505e922970ca477eb1cf4542c88c7f545e65b7fdaa76
5
+ SHA512:
6
+ metadata.gz: a8b16de65cd28d1af9afd4c7be2e3ec52df3acaa093d56a1c635ddc854dda94eb0030b4efdf171bec88716f366737a875aeeff7833b1748f236f0184c5d1e1e9
7
+ data.tar.gz: 2d1071f20224c557ec04ff41024ff28c1a65653d53ffa6bd67b8b45aa9c349a3a53ac15cbffeb3e6c9b3f4a53edaea8005597ea76a65ae269d870a1fb95ed445
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /Gemfile.lock
10
+ /.ruby-version
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,15 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ Exclude:
4
+ - 'bin/*'
5
+
6
+ Metrics/LineLength:
7
+ Enabled: false
8
+
9
+ Metrics/BlockLength:
10
+ Exclude:
11
+ - 'spec/**/*'
12
+ Style/TrailingCommaInLiteral:
13
+ EnforcedStyleForMultiline: comma
14
+ Style/RescueStandardError:
15
+ EnforcedStyle: implicit
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5
5
+ - 2.4
6
+ - 2.3
7
+ before_install: gem install bundler -v 1.16.0
8
+ script:
9
+ - 'bin/rspec'
10
+ - 'bin/rubocop'
data/Gemfile ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ group :development do
8
+ gem 'rubocop'
9
+ end
10
+
11
+ group :test do
12
+ gem 'webmock'
13
+ end
14
+
15
+ group :development, :test do
16
+ gem 'pry'
17
+ gem 'pry-byebug'
18
+ end
19
+
20
+ # Specify your gem's dependencies in oembed_proxy.gemspec
21
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 William Johnston
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.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # OembedProxy
2
+
3
+ [![Build Status](https://travis-ci.org/APMG/oembed_proxy.svg?branch=master)](https://travis-ci.org/APMG/oembed_proxy)
4
+
5
+ Library to manage multiple oEmbed providers, including officially supported oEmbeds, custom implemented "fauxembeds", and Embed.ly providers.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'oembed_proxy'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install oembed_proxy
22
+
23
+ ## Short Example
24
+
25
+ ```ruby
26
+ require 'oembed_proxy'
27
+
28
+ my_url = 'https://www.youtube.com/watch?v=yxMxQrX6dOQ'
29
+ handler = OembedProxy::Handler.new(
30
+ OembedProxy::FirstParty.new,
31
+ OembedProxy::AssociatedPress.new,
32
+ OembedProxy::FusiontableMap.new,
33
+ OembedProxy::GoogleDocument.new,
34
+ OembedProxy::GoogleMapsengine.new,
35
+ OembedProxy::GoogleSpreadsheet.new,
36
+ OembedProxy::Tableau.new,
37
+ OembedProxy::Embedly.new('myembedlykey'),
38
+ )
39
+ handler.handles_url?(my_url) # => true
40
+ handler.get_data(my_url) # =>
41
+ # {
42
+ # "type" => "video",
43
+ # "thumbnail_height" => 360,
44
+ # "thumbnail_url" => "https://i.ytimg.com/vi/yxMxQrX6dOQ/hqdefault.jpg",
45
+ # "provider_url" => "https://www.youtube.com/",
46
+ # "thumbnail_width" => 480,
47
+ # "title" => "Inside MPR News",
48
+ # "author_url" => "https://www.youtube.com/user/AmericanPublicMedia",
49
+ # "provider_name" => "YouTube",
50
+ # "width" => 480,
51
+ # "author_name" => "AmericanPublicMedia",
52
+ # "height" => 270,
53
+ # "version" => "1.0",
54
+ # "html" => "<iframe width=\"480\" height=\"270\" src=\"https://www.youtube.com/embed/yxMxQrX6dOQ?feature=oembed\" frameborder=\"0\" allow=\"autoplay; encrypted-media\" allowfullscreen></iframe>"
55
+ # }
56
+ ```
57
+
58
+ ## Architecture
59
+
60
+ Each provider and the handler implement the following API:
61
+
62
+ * `#handles_url?(url)`
63
+ * `#get_data(url, other_params)`
64
+
65
+ The `#handles_url?(url)` method takes a URL and returns a boolean describing whether a given provider (or the composition of the registered providers in the case of the handler) supports the given URL.
66
+
67
+ The `#get_data(url, other_params)` method takes a URL and optional additional hash params (like `width:` and `height:`), and then returns a hash containing the oembed response.
68
+
69
+ ### Handler
70
+
71
+ In addition to the two methods above, the handler also allows registration of provider classes. This can be done by providing a list of providers to the constructor:
72
+
73
+ ```ruby
74
+ handler = OembedProxy::Handler.new([
75
+ OembedProxy::FirstParty.new,
76
+ OembedProxy::AssociatedPress.new,
77
+ ])
78
+ ```
79
+
80
+ Or this can be done by calling `#register(provider)`:
81
+
82
+ ```ruby
83
+ handler = OembedProxy::Handler.new
84
+ handler.register OembedProxy::FirstParty.new
85
+ handler.register OembedProxy::AssociatedPress.new
86
+ ```
87
+
88
+ Once providers have been registered, calling `#handles_url?` and `#get_data` on the handler will operate on the composition of the registered providers.
89
+
90
+ ### Implementing your own providers
91
+
92
+ You are able to easily implement additional providers by creating an object which implements the `#handles_url?(url)` and `#get_data(url, other_params)` methods. Take a look at the existing provider classes for examples.
93
+
94
+ ## Development
95
+
96
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. Before sending a pull request, you will want to run `bin/rubocop` to lint your work.
97
+
98
+ 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).
99
+
100
+ ## Contributing
101
+
102
+ Bug reports and pull requests are welcome on GitHub at https://github.com/APMG/oembed_proxy.
103
+
104
+ ## License
105
+
106
+ 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
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'oembed_proxy'
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__)
data/bin/rspec ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
12
+ load(bundle_binstub) if File.file?(bundle_binstub)
13
+
14
+ require "pathname"
15
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
16
+ Pathname.new(__FILE__).realpath)
17
+
18
+ require "rubygems"
19
+ require "bundler/setup"
20
+
21
+ load Gem.bin_path("rspec-core", "rspec")
data/bin/rubocop ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
12
+ load(bundle_binstub) if File.file?(bundle_binstub)
13
+
14
+ require "pathname"
15
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
16
+ Pathname.new(__FILE__).realpath)
17
+
18
+ require "rubygems"
19
+ require "bundler/setup"
20
+
21
+ load Gem.bin_path("rubocop", "rubocop")
data/bin/setup ADDED
@@ -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,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OembedProxy
4
+ # Associated Press Interactives Fauxembed
5
+ class AssociatedPress
6
+ AP_REGEX = %r{\Ahttps?:\/\/(?:hosted\.ap\.org\/interactives|interactives\.ap\.org)\/.+}
7
+
8
+ def handles_url?(url)
9
+ !AP_REGEX.match(url).nil?
10
+ end
11
+
12
+ def get_data(url, _other_params = {})
13
+ return nil unless handles_url? url
14
+
15
+ oembed = {}
16
+
17
+ oembed['type'] = 'rich'
18
+ oembed['version'] = '1.0'
19
+
20
+ oembed['provider_name'] = 'Associated Press'
21
+ oembed['provider_url'] = 'http://www.ap.org/'
22
+
23
+ oembed['html'] = '<iframe class="ap-embed" width="100%" height="600" frameborder="0" scrolling="yes" marginheight="0" marginwidth="0" src="' + url + '"></iframe>'
24
+ oembed['width'] = 600
25
+ oembed['height'] = 600
26
+
27
+ oembed
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'oembed_proxy/first_party'
4
+ require 'oembed_proxy/utility'
5
+
6
+ module OembedProxy
7
+ # Embedly provider
8
+ class Embedly < FirstParty
9
+ EMBEDLY_URL = 'https://api.embed.ly/1/oembed'
10
+
11
+ def initialize(embedly_key)
12
+ # Import the expected embed.ly providers.
13
+ @pattern_hash = {}
14
+ @embedly_key = embedly_key
15
+
16
+ File.open('lib/providers/embedly_patterns.def', 'r') do |f|
17
+ f.each do |line|
18
+ regex = Utility.clean_pattern(line)
19
+ @pattern_hash[regex] = EMBEDLY_URL
20
+ end
21
+ end
22
+ end
23
+
24
+ def get_data(url, other_params = {})
25
+ other_params[:key] = @embedly_key
26
+ super
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'cgi'
5
+ require 'net/http'
6
+ require 'json'
7
+
8
+ require 'oembed_proxy/utility'
9
+ require 'oembed_proxy/oembed_exception'
10
+
11
+ module OembedProxy
12
+ # Handles all sites with officially supported oEmbed providers
13
+ class FirstParty
14
+ USER_AGENT = 'Ruby oEmbed Proxy'
15
+
16
+ def initialize
17
+ # Import the expected first party providers.
18
+ @pattern_hash = {}
19
+
20
+ yaml_hash = YAML.load_file('lib/providers/first_party.yml')
21
+ yaml_hash.each_value do |hsh|
22
+ hsh['pattern_list'].each do |pattern|
23
+ regex = Utility.clean_pattern(pattern)
24
+ @pattern_hash[regex] = hsh['endpoint']
25
+ end
26
+ end
27
+ end
28
+
29
+ def handles_url?(url)
30
+ !find_endpoint(url).nil?
31
+ end
32
+
33
+ def get_data(url, other_params = {})
34
+ endpoint = find_endpoint(url)
35
+ return nil if endpoint.nil?
36
+
37
+ uri = URI(endpoint)
38
+ new_params = { url: url, format: 'json' }
39
+ new_params.merge! other_params
40
+ # Merge in existing params.
41
+ new_params.merge! CGI.parse(uri.query) if uri.query
42
+ uri.query = URI.encode_www_form(new_params)
43
+
44
+ fetch(uri)
45
+ end
46
+
47
+ private
48
+
49
+ ERROR_CLASS_MAPPING = {
50
+ Net::HTTPBadRequest => '400 Bad Request',
51
+ Net::HTTPUnauthorized => '401 Unauthorized',
52
+ Net::HTTPForbidden => '403 Forbidden',
53
+ Net::HTTPNotFound => '404 Not Found',
54
+ Net::HTTPInternalServerError => '500 Internal Server Error',
55
+ Net::HTTPNotImplemented => '501 Not Implemented',
56
+ Net::HTTPServiceUnavailable => '503 Service Unavailable',
57
+ }.freeze
58
+
59
+ MAX_REDIRECTS = 10
60
+
61
+ def fetch(uri, times_recursed: 0) # rubocop:disable Metrics/MethodLength
62
+ raise OembedException, '500 Internal Server Error' if times_recursed > MAX_REDIRECTS
63
+
64
+ res = request_builder(uri)
65
+
66
+ case res
67
+ when Net::HTTPSuccess
68
+ JSON[res.body]
69
+ when Net::HTTPMovedPermanently, Net::HTTPFound
70
+ fetch(URI(res['location']), times_recursed: (times_recursed + 1))
71
+ else
72
+ raise OembedException, (ERROR_CLASS_MAPPING[res.class] || "Unknown response: #{res.class}")
73
+ end
74
+ rescue JSON::ParserError
75
+ return nil
76
+ end
77
+
78
+ def request_builder(uri)
79
+ req = Net::HTTP::Get.new(uri)
80
+ req['User-Agent'] = USER_AGENT
81
+
82
+ Net::HTTP.start(uri.hostname, uri.port, use_ssl: (uri.scheme == 'https')) do |http|
83
+ http.request(req)
84
+ end
85
+ end
86
+
87
+ def find_endpoint(url)
88
+ @pattern_hash.each_key do |p|
89
+ return @pattern_hash[p] if url.to_s.match(p)
90
+ end
91
+
92
+ # If all else fails, return nil.
93
+ nil
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OembedProxy
4
+ # Google Apps Fusiontable Map Fauxembeds
5
+ class FusiontableMap
6
+ FUSIONTABLE_REGEX = %r{\Ahttps://www\.google\.com/fusiontables.+}
7
+
8
+ def handles_url?(url)
9
+ !(url =~ FUSIONTABLE_REGEX).nil?
10
+ end
11
+
12
+ def get_data(url, _other_params = {})
13
+ return nil unless handles_url? url
14
+
15
+ oembed = {}
16
+
17
+ oembed['type'] = 'rich'
18
+ oembed['version'] = '1.0'
19
+
20
+ oembed['provider_name'] = 'Google Apps Fusion Tables'
21
+ oembed['provider_url'] = 'https://www.google.com/drive/apps.html#fusiontables'
22
+
23
+ oembed['html'] = '<iframe class="google-map" width="100%" height="500" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="' + url + '"></iframe>'
24
+ oembed['width'] = 500
25
+ oembed['height'] = 500
26
+
27
+ oembed
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OembedProxy
4
+ # Google Docs Fauxembed
5
+ class GoogleDocument
6
+ GOOGLE_DOCUMENT_REGEX = %r{\Ahttps://docs\.google\.com/document.+}
7
+
8
+ def handles_url?(url)
9
+ !(url =~ GOOGLE_DOCUMENT_REGEX).nil?
10
+ end
11
+
12
+ def get_data(url, _other_params = {})
13
+ return nil unless handles_url? url
14
+
15
+ oembed = {}
16
+
17
+ oembed['type'] = 'rich'
18
+ oembed['version'] = '1.0'
19
+
20
+ oembed['provider_name'] = 'Google Apps Documents'
21
+ oembed['provider_url'] = 'https://docs.google.com/document/‎'
22
+
23
+ oembed['html'] = '<iframe class="google-docs document" width="100%" height="500" frameborder="0" scrolling="yes" marginheight="0" marginwidth="0" src="' + url + '"></iframe>'
24
+ oembed['width'] = 500
25
+ oembed['height'] = 500
26
+
27
+ oembed
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OembedProxy
4
+ # Google Maps Engine Fauxembed
5
+ class GoogleMapsengine
6
+ MAPSENGINE_REGEXES = [
7
+ %r{\Ahttps://mapsengine\.google\.com/map/(?:edit|view)\?mid=(.+)},
8
+ %r{\Ahttps://www\.google\.com/maps/d/edit\?mid=(.+)},
9
+ ].freeze
10
+
11
+ def handles_url?(url)
12
+ !get_matching_regex(url).nil?
13
+ end
14
+
15
+ def get_data(url, _other_params = {})
16
+ return nil unless handles_url? url
17
+
18
+ {
19
+ 'type' => 'rich',
20
+ 'version' => '1.0',
21
+ 'provider_name' => 'Google Maps Engine',
22
+ 'provider_url' => 'https://mapsengine.google.com/',
23
+ 'html' => "<iframe class=\"google-map\" width=\"640\" height=\"480\" frameborder=\"0\" scrolling=\"no\" marginheight=\"0\" marginwidth=\"0\" src=\"https://mapsengine.google.com/map/embed?mid=#{url.match(get_matching_regex(url))[1]}\"></iframe>",
24
+ 'width' => 500,
25
+ 'height' => 500,
26
+ }
27
+ end
28
+
29
+ private
30
+
31
+ def get_matching_regex(str)
32
+ MAPSENGINE_REGEXES.each do |regex|
33
+ return regex unless (str =~ regex).nil?
34
+ end
35
+
36
+ nil
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OembedProxy
4
+ # Google Spreadsheet Fauxembed
5
+ class GoogleSpreadsheet
6
+ GOOGLE_SPREADSHEET_REGEX = %r{\Ahttps://docs\.google\.com/spreadsheet.+}
7
+
8
+ def handles_url?(url)
9
+ !(url =~ GOOGLE_SPREADSHEET_REGEX).nil?
10
+ end
11
+
12
+ def get_data(url, _other_params = {})
13
+ return nil unless handles_url? url
14
+
15
+ oembed = {}
16
+
17
+ oembed['type'] = 'rich'
18
+ oembed['version'] = '1.0'
19
+
20
+ oembed['provider_name'] = 'Google Apps Spreadsheets'
21
+ oembed['provider_url'] = 'https://docs.google.com/spreadsheet/‎'
22
+
23
+ oembed['html'] = '<iframe class="google-docs spreadsheet" width="100%" height="500" frameborder="0" scrolling="yes" marginheight="0" marginwidth="0" src="' + url + '&amp;output=html"></iframe>'
24
+ oembed['width'] = 500
25
+ oembed['height'] = 500
26
+
27
+ oembed
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OembedProxy
4
+ # Generic composing handler
5
+ class Handler
6
+ def initialize(*providers)
7
+ @registered_providers = providers
8
+ end
9
+
10
+ def register(provider)
11
+ @registered_providers << provider
12
+ end
13
+
14
+ def handles_url?(url)
15
+ !provider_for_url(url).nil?
16
+ end
17
+
18
+ def get_data(url, other_params = {})
19
+ provider = provider_for_url(url)
20
+ return provider.get_data(url, other_params) unless provider.nil?
21
+ end
22
+
23
+ private
24
+
25
+ def provider_for_url(url)
26
+ # TODO: Switch to #find
27
+ @registered_providers.select { |p| p.handles_url? url }.first
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OembedProxy
4
+ # A couple of things pulled in from ActiveSupport, but using refinements
5
+ # instead of monkeypatching.
6
+ module InactiveSupport
7
+ refine String do
8
+ def parameterize(sep = '-')
9
+ parameterized_string = dup.downcase
10
+ # Turn unwanted chars into the separator
11
+ parameterized_string.gsub!(/[^a-z0-9\-_]+/, sep)
12
+ unless sep.nil? || sep.empty?
13
+ re_sep = Regexp.escape(sep)
14
+ # No more than one of the separator in a row.
15
+ parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
16
+ # Remove leading/trailing separator.
17
+ parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/, '')
18
+ end
19
+ parameterized_string.downcase
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OembedProxy
4
+ class OembedException < StandardError; end
5
+ end