oh-my-embed 1.0.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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +9 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +164 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +17 -0
  10. data/bin/setup +8 -0
  11. data/lib/oh_my_embed.rb +52 -0
  12. data/lib/oh_my_embed/crawler.rb +77 -0
  13. data/lib/oh_my_embed/provider.rb +116 -0
  14. data/lib/oh_my_embed/providers/facebook_post.rb +20 -0
  15. data/lib/oh_my_embed/providers/facebook_video.rb +15 -0
  16. data/lib/oh_my_embed/providers/flickr.rb +15 -0
  17. data/lib/oh_my_embed/providers/instagram.rb +15 -0
  18. data/lib/oh_my_embed/providers/kickstarter.rb +15 -0
  19. data/lib/oh_my_embed/providers/slideshare.rb +19 -0
  20. data/lib/oh_my_embed/providers/soundcloud.rb +14 -0
  21. data/lib/oh_my_embed/providers/spotify.rb +17 -0
  22. data/lib/oh_my_embed/providers/twitter.rb +14 -0
  23. data/lib/oh_my_embed/providers/youtube.rb +18 -0
  24. data/lib/oh_my_embed/response.rb +110 -0
  25. data/lib/oh_my_embed/version.rb +3 -0
  26. data/oh-my-embed.gemspec +33 -0
  27. data/spec/fixtures/vcr_cassettes/facebook_posts.yml +65 -0
  28. data/spec/fixtures/vcr_cassettes/facebook_videos.yml +65 -0
  29. data/spec/fixtures/vcr_cassettes/flickr.yml +90 -0
  30. data/spec/fixtures/vcr_cassettes/instagram.yml +75 -0
  31. data/spec/fixtures/vcr_cassettes/kickstarter.yml +65 -0
  32. data/spec/fixtures/vcr_cassettes/slideshare.yml +87 -0
  33. data/spec/fixtures/vcr_cassettes/soundcloud.yml +57 -0
  34. data/spec/fixtures/vcr_cassettes/spotify.yml +44 -0
  35. data/spec/fixtures/vcr_cassettes/twitter.yml +66 -0
  36. data/spec/fixtures/vcr_cassettes/youtube.yml +57 -0
  37. data/spec/oh_my_embed/crawler_spec.rb +99 -0
  38. data/spec/oh_my_embed/provider_spec.rb +103 -0
  39. data/spec/oh_my_embed/providers/facebook_post_spec.rb +40 -0
  40. data/spec/oh_my_embed/providers/facebook_video_spec.rb +40 -0
  41. data/spec/oh_my_embed/providers/flickr_spec.rb +44 -0
  42. data/spec/oh_my_embed/providers/instagram_spec.rb +44 -0
  43. data/spec/oh_my_embed/providers/kickstarter_spec.rb +44 -0
  44. data/spec/oh_my_embed/providers/slideshare_spec.rb +44 -0
  45. data/spec/oh_my_embed/providers/soundcloud_spec.rb +44 -0
  46. data/spec/oh_my_embed/providers/spotify_spec.rb +41 -0
  47. data/spec/oh_my_embed/providers/twitter_spec.rb +40 -0
  48. data/spec/oh_my_embed/providers/youtube_spec.rb +44 -0
  49. data/spec/oh_my_embed/response_spec.rb +174 -0
  50. data/spec/oh_my_embed_spec.rb +7 -0
  51. data/spec/spec_helper.rb +17 -0
  52. metadata +234 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3e9b3c0abc85a8a6452fe5b72c9ec7b09584cd6b
4
+ data.tar.gz: 352afe1e30344a73c37a8e4158c98187cc48d271
5
+ SHA512:
6
+ metadata.gz: f9b7bd55bb85ea38c46bce13e3e86759477b0dc753175aa7a9f2d3d434eac911d3b55157a5e2bdc10d325482dfd40080516bd334272badc761a2eeae9e85fb41
7
+ data.tar.gz: 2966e93abe6f4ceba68a8f78922b792c2cb85324bcbc8e13a3be0066785f8746d7e78c85e093bfd0403ea5de77d6aded02c21eee1ad4624b84345095179ef337
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ sudo: false
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.2.3
6
+ before_install: gem install bundler -v 1.12.2
7
+ addons:
8
+ code_climate:
9
+ repo_token: b79288caf70d78fded384188834e29819e9cb2fcb039592b5b825af39b612d89
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in oh-my-embed.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Axel Wahlen
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,164 @@
1
+ OhMyEmbed
2
+ =================
3
+ VERSION BADGE
4
+ [![Build Status](https://travis-ci.org/dino115/oh-my-embed.svg?branch=master)](https://travis-ci.org/dino115/oh-my-embed)
5
+ [![Code Climate](https://codeclimate.com/github/dino115/oh-my-embed/badges/gpa.svg)](https://codeclimate.com/github/dino115/oh-my-embed)
6
+ [![Test Coverage](https://codeclimate.com/github/dino115/oh-my-embed/badges/coverage.svg)](https://codeclimate.com/github/dino115/oh-my-embed/coverage)
7
+ [![Dependency Status](https://gemnasium.com/badges/github.com/dino115/oh-my-embed.svg)](https://gemnasium.com/github.com/dino115/oh-my-embed)
8
+
9
+ Simple gem to interact with oembed providers. Read specs at http://www.oembed.com
10
+
11
+ ** WORK IN PROGRESS, not finshed yet **
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'oh-my-embed'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install oh-my-embed
28
+
29
+ ## Build in providers
30
+
31
+ - Youtube
32
+ - Slideshare
33
+ - Instagram
34
+ - Twitter
35
+ - Facebook (Posts)
36
+ - Facebook (Videos)
37
+ - Flickr
38
+ - SoundCloud
39
+ - Kickstarter
40
+ - Spotify
41
+
42
+ If you need some other providers feel free to add them via pull request or build it for your application only by using the `OhMyEmbed::Provider` base class.
43
+ See the custom provider section for more informations.
44
+
45
+ ## Usage
46
+
47
+ ### Basics
48
+ First of all you have to create a `OhMyEmbed::Crawler` object and register your desired providers.
49
+ You can use all build-in providers, select only a few or mix them up with your custom providers.
50
+
51
+ ```ruby
52
+ # Crawler with all build-in providers (includes all classes you added to the OhMyEmbed::Providers module)
53
+ crawler = OhMyEmbed::Crawler.new(all: true)
54
+
55
+ # Crawler with specific providers
56
+ crawler = OhMyEmbed::Crawler.new(:youtube, :slideshare)
57
+
58
+ # Crawler with custom providers
59
+ crawler = OhMyEmbed::Crawler.new(:youtube, MyProvider)
60
+
61
+ # Crawler with all build-in and a custom provider
62
+ crawler = OhMyEmbed::Crawler.new(MyProvider, all: true)
63
+ ```
64
+
65
+ Now you can crawl for your embed code easily with the `fetch` method and get an `OhMyEmbed::Response` object.
66
+
67
+ ```ruby
68
+ result = crawler.fetch('https://www.youtube.com/watch?v=EErY75MXYXI')
69
+
70
+ # with additional options, piped as parameter to the endpoint
71
+ result = crawler.fetch('https://www.youtube.com/watch?v=EErY75MXYXI', autoplay: 1)
72
+ ```
73
+
74
+ You also can also ask if the url matches an registered url schema or get the provider.
75
+
76
+ ```ruby
77
+ # check if url is embeddable
78
+ crawler.embeddable?('https://www.youtube.com/watch?v=EErY75MXYXI') # => true
79
+
80
+ # get the provider class
81
+ crawler.provider_for('https://www.youtube.com/watch?v=EErY75MXYXI') # => OhMyEmbed::Providers::Youtube
82
+ ```
83
+
84
+ If you want use only a single provider, then you can perform your fetch action directly on the provider too.
85
+
86
+ ```ruby
87
+ OhMyEmbed::Providers::Youtube.fetch('https://www.youtube.com/watch?v=EErY75MXYXI')
88
+ ```
89
+
90
+ ### Response handling
91
+ With a response object you have access to all oembed fields, described in http://oembed.com/#section2
92
+
93
+ To access the raw response you can use the the `attribute` method.
94
+
95
+ ```ruby
96
+ response.attributes # return all raw attributes as hash
97
+ response.attribute(:thumbnail_url) # get a single attribute from the raw response
98
+ ```
99
+
100
+ A response object provides an easy interface to the different response types and provider specific fields. (i.e. slideshare provides a thumbnail field instead of thumbnail_url)
101
+
102
+ ```ruby
103
+ response.type # [Symbol] one of :photo, :video, :link, :rich
104
+ response.provider # [OhMyEmbed::Provider]
105
+ response.provider_name # [String] Provider name from response or readable class name
106
+ response.provider_url # [String|nil]
107
+ response.url # [String] The provided url or original url
108
+ response.author # [Hash|nil] { name: [String], url: [String] }
109
+ response.thumbnail # [Hash|nil] { url: [String|nil], width: [Float], height: [Float] }
110
+ response.embed # [Hash] { html: [String|nil] Some html embed code, url: [String|nil], width: [Float|nil], height: [Float|nil] }
111
+ ```
112
+
113
+ ### Error handling
114
+ The gem can raise the following errors. All of them are subclasses of a generic `OhMyEmbed::Error` class.
115
+
116
+ - *OhMyEmbed::UnknownProvider* when you try to register an unknown provider
117
+ - *OhMyEmbed::ProviderNotFound* when no provider matches the schema of given content url
118
+ - *OhMyEmbed::NotFound* when the endpoint returns with 404 not found
119
+ - *OhMyEmbed::PermissionDenied* when the endpoint returns with 401 unauthorized
120
+ - *OhMyEmbed::FormatNotSupported* when the endpoint returns with 501 not implemented
121
+ - *OhMyEmbed::ParseError* when the parser failed, i.e. the responded json is malformed
122
+
123
+ ### Custom providers
124
+ You can create your own provider class by extending the `OhMyEmbed::Provider` base class.
125
+
126
+ ```ruby
127
+ class MyProvider < OhMyEmbed::Provider
128
+ # The oembed endpoint must be specified
129
+ self.endpoint = 'https://www.example.com/oembed'
130
+
131
+ # The oembed url schemes must be specified, they are used for easy lookup
132
+ self.schemes = [
133
+ '//example.com/content/*'
134
+ ]
135
+
136
+ # Optional you can define the provider name, if not specified, the name will extracted from the class name
137
+ self.provider_name = 'My Provider'
138
+
139
+ # Optional you can use a custom mapping for the response, if your provider doesn't stick to the specification :(
140
+ # this merges with the default mapping, so you must only set the divergent mapping
141
+ self.custom_mapping = {
142
+ 'thumbnail.url' => 'thumbnail'
143
+ }
144
+ end
145
+ ```
146
+
147
+ ## Format
148
+ Currently the gem supports only json as response format.
149
+
150
+ ## Development
151
+
152
+ 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.
153
+
154
+ 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).
155
+
156
+ ## Contributing
157
+
158
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dino115/oh-my-embed. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
159
+
160
+
161
+ ## License
162
+
163
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
164
+
data/Rakefile ADDED
@@ -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
data/bin/console ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'oh_my_embed'
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
+ require 'irb/completion'
15
+ require 'irb/ext/history'
16
+
17
+ IRB.start
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,52 @@
1
+ require 'active_support/all'
2
+
3
+ Dir.glob(File.join(__dir__, 'oh_my_embed', '*.rb'), &method(:require))
4
+ Dir.glob(File.join(__dir__, 'oh_my_embed', '{providers}', '*.rb'), &method(:require))
5
+
6
+ module OhMyEmbed
7
+ # All OhMyEmbed errors inherits from a generic OhMyEmbed::Error class
8
+ #
9
+ # - OhMyEmbed::UnknownProvider
10
+ # - OhMyEmbed::ProviderNotFound
11
+ # - OhMyEmbed::NotFound
12
+ # - OhMyEmbed::PermissionDenied
13
+ # - OhMyEmbed::FormatNotSupported
14
+ # - OhMyEmbed::ParseError
15
+ class Error < StandardError; end
16
+
17
+ class UnknownProvider < OhMyEmbed::Error # nodoc #
18
+ def self.new(provider)
19
+ super("Can't register unknown provider '#{provider}'")
20
+ end
21
+ end
22
+
23
+ class ProviderNotFound < OhMyEmbed::Error # nodoc #
24
+ def self.new(url)
25
+ super("No provider found for given content_url (#{url}). Do you have registered the associated provider?")
26
+ end
27
+ end
28
+
29
+ class NotFound < OhMyEmbed::Error # nodoc #
30
+ def self.new(url)
31
+ super("No embed code for this content (#{url}) found")
32
+ end
33
+ end
34
+
35
+ class PermissionDenied < OhMyEmbed::Error # nodoc #
36
+ def self.new(url)
37
+ super("You don't have permissions to access the embed code for this content (#{url})")
38
+ end
39
+ end
40
+
41
+ class FormatNotSupported < OhMyEmbed::Error # nodoc #
42
+ def self.new(provider_name)
43
+ super("The provider '#{provider_name}' doesn't support json")
44
+ end
45
+ end
46
+
47
+ class ParseError < OhMyEmbed::Error # nodoc #
48
+ def self.new(provider_name, url, data_string)
49
+ super("Parsing failed for content (#{data_string}); Provider: #{provider_name}; URL: #{url}")
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,77 @@
1
+ module OhMyEmbed
2
+ class Crawler
3
+ attr_reader :providers
4
+
5
+ # Initialize the OhMyEmbed::Crawler
6
+ #
7
+ # @param *providers [Symbol|OhMyEmbed::Provider|Hash] the providers to register or options as the last argument
8
+ def initialize(*providers)
9
+ @providers = Set.new
10
+
11
+ options = providers.extract_options!
12
+
13
+ register_all_build_in_providers if options[:all]
14
+ providers.each do |provider|
15
+ register provider
16
+ end
17
+ end
18
+
19
+ # Register a provider
20
+ #
21
+ # @param provider [Symbol|OhMyEmbed::Provider]
22
+ # @raise [OhMyEmbed::UnknownProvider] if you try to register an unknown provider
23
+ def register(provider)
24
+ if provider.is_a? Symbol
25
+ begin
26
+ provider = OhMyEmbed::Providers.const_get(provider.to_s.classify)
27
+ rescue NameError
28
+ raise OhMyEmbed::UnknownProvider.new(provider)
29
+ end
30
+ end
31
+
32
+ @providers.add provider
33
+ end
34
+
35
+ # Register all build in providers for this crawler
36
+ #
37
+ def register_all_build_in_providers
38
+ build_in_providers.each do |provider|
39
+ register provider
40
+ end
41
+ end
42
+
43
+ # Fetch the embed response for the given content_url
44
+ #
45
+ # @param [String] content_url
46
+ # @return [OhMyEmbed::Response]
47
+ def fetch(content_url)
48
+ provider = self.provider_for(content_url)
49
+ provider.fetch(content_url)
50
+ end
51
+
52
+ # Get the provider for given content_url
53
+ #
54
+ # @param [String] content_url
55
+ # @raise [OhMyEmbed::UnknownProvider] if no registered provider matches the given context_url
56
+ # @return [OhMyEmbed::Provider]
57
+ def provider_for(content_url)
58
+ @providers.find{ |provider| provider.regex =~ content_url } || raise(OhMyEmbed::ProviderNotFound.new(content_url))
59
+ end
60
+
61
+ # Check if any provider matches the given content_url
62
+ #
63
+ # @param [String] content_url
64
+ # @return [true|false]
65
+ def embeddable?(content_url)
66
+ @providers.any?{ |provider| provider.regex =~ content_url }
67
+ end
68
+
69
+ # Select all provider constants from the OhMyEmbed::Providers module
70
+ #
71
+ # @return [Array<Symbol>]
72
+ def build_in_providers
73
+ OhMyEmbed::Providers.constants
74
+ .select { |c| OhMyEmbed::Providers.const_get(c) < OhMyEmbed::Provider }
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,116 @@
1
+ module OhMyEmbed
2
+ ##
3
+ # Provider
4
+ class Provider
5
+ class_attribute :provider_name
6
+ class_attribute :endpoint
7
+ class_attribute :schemes
8
+ class_attribute :custom_mapping
9
+
10
+ self.provider_name = nil
11
+ self.endpoint = nil
12
+ self.custom_mapping = {}
13
+ self.schemes = []
14
+
15
+ def self.default_mapping
16
+ {
17
+ 'type' => 'type',
18
+ 'provider_name' => 'provider_name',
19
+ 'provider_url' => 'provider_url',
20
+ 'url' => 'url',
21
+ 'title' => 'title',
22
+ 'author.name' => 'author_name',
23
+ 'author.url' => 'author_url',
24
+ 'thumbnail.url' => 'thumbnail_url',
25
+ 'thumbnail.width' => 'thumbnail_width',
26
+ 'thumbnail.height' => 'thumbnail_height',
27
+ 'embed.html' => 'html',
28
+ 'embed.width' => 'width',
29
+ 'embed.height' => 'height',
30
+ }
31
+ end
32
+
33
+ # Fetch the embed result for the provider content
34
+ #
35
+ # @param [String] url
36
+ # @param [Hash] params
37
+ #
38
+ # @raise [OhMyEmbed::Error] on request timeout
39
+ # @raise [OhMyEmbed::PermissionDenied] on request status 401
40
+ # @raise [OhMyEmbed::NotFound] on request status 404
41
+ # @raise [OhMyEmbed::FormatNotSupported] on request status 501
42
+ # @raise [OhMyEmbed::ParseError] on parsing error
43
+ #
44
+ # @return [OhMyEmbed::Response]
45
+ def self.fetch(url, params = {})
46
+ uri = URI.parse(self.endpoint)
47
+ uri.query = URI.encode_www_form(params.merge({
48
+ url: url,
49
+ format: 'json',
50
+ }))
51
+
52
+ connection = Net::HTTP.new(uri.host, uri.port)
53
+ connection.use_ssl = (uri.port == URI::HTTPS::DEFAULT_PORT)
54
+
55
+ begin
56
+ response = connection.get(uri.request_uri)
57
+ rescue Timeout::Error
58
+ raise OhMyEmbed::Error.new('Request timed out')
59
+ end
60
+
61
+ case response
62
+ when Net::HTTPUnauthorized
63
+ raise OhMyEmbed::PermissionDenied.new(url)
64
+ when Net::HTTPNotFound
65
+ raise OhMyEmbed::NotFound.new(url)
66
+ when Net::HTTPNotImplemented
67
+ raise OhMyEmbed::FormatNotSupported.new(self.name)
68
+ when Net::HTTPOK
69
+ begin
70
+ response_data = JSON.parse(response.body)
71
+ rescue
72
+ raise OhMyEmbed::ParseError.new(self.name, url, response.body)
73
+ end
74
+ end
75
+
76
+ OhMyEmbed::Response.new(self, url, response_data)
77
+ end
78
+
79
+ # Get a union regex to match the providers url schemes
80
+ #
81
+ # @return [Regexp]
82
+ def self.regex
83
+ @_regex ||= begin
84
+ regexes = self.schemes.map(&method(:regexify))
85
+ Regexp.union regexes
86
+ end
87
+ end
88
+
89
+ # Create a regular expression from an url schema, does nothing if the schema is already a Regexp
90
+ #
91
+ # @param [String|Regexp] schema
92
+ # @return [Regexp]
93
+ def self.regexify(schema)
94
+ if schema.is_a? Regexp
95
+ schema
96
+ else
97
+ schema = "(https:|http:)#{schema}" unless schema.start_with?('http')
98
+ Regexp.new("^#{schema.gsub('.', '\.').gsub('*', '(.*?)')}$", Regexp::IGNORECASE)
99
+ end
100
+ end
101
+
102
+ # Get the mapping hash, defaults merged with the custom mapping
103
+ #
104
+ # @return [Hash]
105
+ def self.mapping
106
+ @_mapping ||= self.default_mapping.merge(self.custom_mapping)
107
+ end
108
+
109
+ # Get the provider name
110
+ #
111
+ # @return [String]
112
+ def self.provider_name
113
+ instance_variable_get('@provider_name') || self.name.split('::').last
114
+ end
115
+ end
116
+ end