billfold 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/README.md +39 -0
- data/Rakefile +26 -0
- data/VERSION +1 -0
- data/app/controllers/billfold/identities_controller.rb +53 -0
- data/app/views/billfold/identities/_list.html.erb +8 -0
- data/app/views/billfold/identities/index.html.erb +2 -0
- data/billfold.gemspec +24 -0
- data/config/locales/en.yml +6 -0
- data/config/routes.rb +11 -0
- data/lib/billfold.rb +56 -0
- data/lib/billfold/active_record_identity.rb +47 -0
- data/lib/billfold/active_record_user.rb +46 -0
- data/lib/billfold/controller_support.rb +51 -0
- data/lib/billfold/engine.rb +25 -0
- data/lib/billfold/identity.rb +99 -0
- data/lib/billfold/user.rb +25 -0
- data/lib/rails/generators/billfold/migration_generator.rb +27 -0
- data/lib/rails/generators/billfold/models_generator.rb +14 -0
- data/lib/rails/generators/billfold/templates/identity.rb +5 -0
- data/lib/rails/generators/billfold/templates/migration.rb +23 -0
- data/lib/rails/generators/billfold/templates/user.rb +5 -0
- data/spec/billfold_spec.rb +23 -0
- data/spec/controllers/identities_controller_spec.rb +31 -0
- data/spec/dummy/.gitignore +5 -0
- data/spec/dummy/README +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/images/rails.png +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/identity.rb +5 -0
- data/spec/dummy/app/models/user.rb +5 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +48 -0
- data/spec/dummy/config/boot.rb +6 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/migrate/20110904204023_create_users_and_identities.rb +23 -0
- data/spec/dummy/db/schema.rb +31 -0
- data/spec/dummy/db/seeds.rb +7 -0
- data/spec/dummy/doc/README_FOR_APP +2 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/lib/tasks/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/index.html +241 -0
- data/spec/dummy/public/robots.txt +5 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/test/fixtures/.gitkeep +0 -0
- data/spec/dummy/test/functional/.gitkeep +0 -0
- data/spec/dummy/test/integration/.gitkeep +0 -0
- data/spec/dummy/test/performance/browsing_test.rb +12 -0
- data/spec/dummy/test/test_helper.rb +13 -0
- data/spec/dummy/test/unit/.gitkeep +0 -0
- data/spec/dummy/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/dummy/vendor/plugins/.gitkeep +0 -0
- data/spec/factories/identity_factories.rb +18 -0
- data/spec/factories/user_factories.rb +7 -0
- data/spec/models/identity_spec.rb +123 -0
- data/spec/spec_helper.rb +30 -0
- metadata +260 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Gemfile.lock
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.9.2
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
## Billfold
|
2
|
+
|
3
|
+
Billfold provides backend Rails support for OmniAuth. Specifically, it
|
4
|
+
routes `/auth/:provider/callback` to
|
5
|
+
`Billfold::IdentitiesController#update_or_create!`, which handles
|
6
|
+
identity management.
|
7
|
+
|
8
|
+
### Requirements
|
9
|
+
|
10
|
+
* Rails 3.x
|
11
|
+
|
12
|
+
### Installation
|
13
|
+
|
14
|
+
1. Add `gem 'billfold'` to your `Gemfile`
|
15
|
+
1. Run `bundle` (or `bundle install`)
|
16
|
+
1. Run `rails g billfold:migration`
|
17
|
+
|
18
|
+
### Configuration
|
19
|
+
|
20
|
+
#### With ActiveRecord
|
21
|
+
|
22
|
+
If you don't have User and Identity model classes, run
|
23
|
+
`rails g billfold:models` to create them. Otherwise, include
|
24
|
+
`Billfold::ActiveRecordUser` and `Billfold::ActiveRecordIdentity` in
|
25
|
+
them respectively. You *may* wish to define
|
26
|
+
`User#perform_additional_merge_operations!` if you need to do additional
|
27
|
+
logic during a user merge.
|
28
|
+
|
29
|
+
#### Without ActiveRecord
|
30
|
+
|
31
|
+
Include `Billfold::User` and `Billfold::Identity` in the model classes.
|
32
|
+
You'll also have do define the following methods:
|
33
|
+
|
34
|
+
* `User.find_by_id(id)`
|
35
|
+
* `User#merge_into!(other_user)`
|
36
|
+
* `Identity.with_provider_and_value(provider, value)`
|
37
|
+
* `Identity#user`
|
38
|
+
* `Identity#update_attributes!`
|
39
|
+
* `Identity#save!`
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rake'
|
10
|
+
require 'rdoc/task'
|
11
|
+
|
12
|
+
require 'rspec/core'
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
RSpec::Core::RakeTask.new(:spec)
|
15
|
+
|
16
|
+
desc "Default: run the unit tests."
|
17
|
+
task :default => [:spec]
|
18
|
+
|
19
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
20
|
+
rdoc.rdoc_dir = 'rdoc'
|
21
|
+
rdoc.title = "Billfold #{File.read './VERSION'}"
|
22
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
23
|
+
rdoc.rdoc_files.include('README.rdoc')
|
24
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
25
|
+
rdoc.rdoc_files.include('app/**/*.rb')
|
26
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Billfold
|
2
|
+
class IdentitiesController < ApplicationController
|
3
|
+
|
4
|
+
before_filter :require_sign_in, :only => [ :index, :destroy ]
|
5
|
+
|
6
|
+
respond_to :html, :json, :xml
|
7
|
+
helper_method :identities, :identity
|
8
|
+
|
9
|
+
def index
|
10
|
+
respond_to do |format|
|
11
|
+
format.html {}
|
12
|
+
format.json { render :json => identities }
|
13
|
+
format.xml { render :xml => identities }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def update_or_create
|
18
|
+
omniauth_hash = request.env['omniauth.auth'] || {}
|
19
|
+
identity = Billfold.identity_class.update_or_create!({
|
20
|
+
:provider => params[:provider],
|
21
|
+
:value => omniauth_hash['uid'],
|
22
|
+
:data => omniauth_hash['user_info'],
|
23
|
+
:user => current_user
|
24
|
+
})
|
25
|
+
self.current_user ||= identity.user
|
26
|
+
respond_to do |format|
|
27
|
+
format.html { redirect_to '/' }
|
28
|
+
format.json { head 201 }
|
29
|
+
format.xml { head 201 }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def destroy
|
34
|
+
identity.destroy
|
35
|
+
respond_to do |format|
|
36
|
+
format.html { redirect_to :index }
|
37
|
+
format.json { head 200 }
|
38
|
+
format.xml { head 200 }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def identities
|
45
|
+
@identities ||= current_user.identities
|
46
|
+
end
|
47
|
+
|
48
|
+
def identity
|
49
|
+
@identity ||= current_user.identities.find_by_id(params[:identity_id] || params[:id])
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/billfold.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = 'billfold'
|
5
|
+
gem.version = File.read('VERSION')
|
6
|
+
gem.description = %q{Identity Management with OmniAuth}
|
7
|
+
gem.summary = gem.description
|
8
|
+
gem.email = ['james.a.rosen@gmail.com']
|
9
|
+
gem.homepage = 'http://github.com/jamesarosen/billfold'
|
10
|
+
gem.authors = ['James A. Rosen']
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.require_paths = ['lib']
|
15
|
+
gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
|
16
|
+
|
17
|
+
gem.add_development_dependency 'rails', '~> 3.1'
|
18
|
+
gem.add_development_dependency 'bundler'
|
19
|
+
gem.add_development_dependency 'mocha'
|
20
|
+
gem.add_development_dependency 'rake'
|
21
|
+
gem.add_development_dependency 'rspec-rails'
|
22
|
+
gem.add_development_dependency 'sqlite3'
|
23
|
+
gem.add_development_dependency 'factory_girl'
|
24
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
|
3
|
+
mount_at = Billfold::Engine.config.mount_at
|
4
|
+
|
5
|
+
post "#{mount_at}auth/:provider/callback" => 'billfold/identities#update_or_create'
|
6
|
+
|
7
|
+
resources :identities, :only => [ :index, :destroy ],
|
8
|
+
:module => 'billfold',
|
9
|
+
:path => "#{mount_at}/identities"
|
10
|
+
|
11
|
+
end
|
data/lib/billfold.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module Billfold
|
2
|
+
|
3
|
+
autoload :ControllerSupport, 'billfold/controller_support'
|
4
|
+
autoload :Identity, 'billfold/identity'
|
5
|
+
autoload :User, 'billfold/user'
|
6
|
+
autoload :ActiveRecordIdentity, 'billfold/active_record_identity'
|
7
|
+
autoload :ActiveRecordUser, 'billfold/active_record_user'
|
8
|
+
|
9
|
+
class <<self
|
10
|
+
# ## Billfold.user_class
|
11
|
+
#
|
12
|
+
# Used by `Billfold::Identity.update_or_create!` when building new users
|
13
|
+
# and `Billfold::ControllerSupport` when looking up the current user
|
14
|
+
# from the session. Calculated from `Billfold.user_class_name`.
|
15
|
+
def user_class
|
16
|
+
constantize user_class_name
|
17
|
+
end
|
18
|
+
|
19
|
+
# Used by `Billfold::ActiveRecordIdentity` for the `belongs_to :user`
|
20
|
+
# association and by `Billfold.user_class` for getting the actual
|
21
|
+
# class. By default, "User"
|
22
|
+
def user_class_name
|
23
|
+
@user_class ||= 'User'
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_writer :user_class_name
|
27
|
+
|
28
|
+
# ## Billfold.identity_class
|
29
|
+
#
|
30
|
+
# Used by `Billfold::IdentitiesController.update_or_create`. Calculated
|
31
|
+
# from `Billfold.identity_class_name`.
|
32
|
+
def identity_class
|
33
|
+
constantize identity_class_name
|
34
|
+
end
|
35
|
+
|
36
|
+
def identity_class_name
|
37
|
+
@identity_class ||= 'Identity'
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_writer :identity_class_name
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def constantize(string)
|
45
|
+
return nil if string.blank?
|
46
|
+
return string.constantize if string.respond_to?(:constantize)
|
47
|
+
string.to_s.split('::').inject(Object) do |memo, name|
|
48
|
+
raise "#{memo}::#{name} does not exist" unless memo.const_defined?(name)
|
49
|
+
memo = memo.const_get(name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
require 'billfold/identity'
|
55
|
+
require 'billfold/engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
|
56
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'billfold/identity'
|
3
|
+
|
4
|
+
module Billfold
|
5
|
+
|
6
|
+
# ## Billfold::ActiveRecordIdentity
|
7
|
+
#
|
8
|
+
# Support for an ActiveRecord Identity class. (Builds on top of
|
9
|
+
# `Billfold::Identity`)
|
10
|
+
module ActiveRecordIdentity
|
11
|
+
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
included do
|
15
|
+
raise "Cannot define identity class until Billfold.user_class_name is set" unless Billfold.user_class_name.present?
|
16
|
+
belongs_to :user, :class_name => ::Billfold.user_class_name
|
17
|
+
serialize :data
|
18
|
+
validates_presence_of :user, :provider, :value
|
19
|
+
|
20
|
+
# There can only be one identity with a given provider and value
|
21
|
+
validates_uniqueness_of :value, :scope => [ :provider ]
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
|
26
|
+
include Billfold::Identity::ClassMethods
|
27
|
+
|
28
|
+
# ### Billfold::Identity.with_provider_and_value
|
29
|
+
#
|
30
|
+
# Return the identity with the given `provider` and `value`, or `nil`,
|
31
|
+
# if no such identity exists. Including classes *must* redefine this
|
32
|
+
# method.
|
33
|
+
def with_provider_and_value(provider, value)
|
34
|
+
where(:provider => provider, :value => value).first
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
module InstanceMethods
|
40
|
+
|
41
|
+
include Billfold::Identity::InstanceMethods
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'billfold/user'
|
3
|
+
|
4
|
+
module Billfold
|
5
|
+
|
6
|
+
# ## Billfold::ActiveRecordUser
|
7
|
+
#
|
8
|
+
# Support for an ActiveRecord User class. (Builds on top of
|
9
|
+
# `Billfold::User`)
|
10
|
+
module ActiveRecordUser
|
11
|
+
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
included do
|
15
|
+
raise "Cannot define user class until Billfold.identity_class_name is set" unless Billfold.identity_class_name.present?
|
16
|
+
validates_presence_of :name
|
17
|
+
has_many :identities, :class_name => ::Billfold.identity_class_name
|
18
|
+
end
|
19
|
+
|
20
|
+
module InstanceMethods
|
21
|
+
|
22
|
+
# Merge this user into another user, deleting this user and moving its
|
23
|
+
# identities to the other.
|
24
|
+
def merge_into!(other)
|
25
|
+
raise ArgumentError.new("#{other} is not a #{Billfold.user_class}") unless other.kind_of?(Billfold.user_class)
|
26
|
+
raise ArgumentError.new("#{other} is not saved") if other.new_record?
|
27
|
+
transaction do
|
28
|
+
identities.update_all({ :user_id => other.id })
|
29
|
+
perform_additional_merge_operations!(other)
|
30
|
+
destroy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# This method is called after all identities have been moved from
|
37
|
+
# `other` to `self`, but before `other` has been destroyed and before the
|
38
|
+
# end of the transaction. By default, it does nothing.
|
39
|
+
def perform_additional_merge_operations!(other)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'billfold'
|
3
|
+
|
4
|
+
module Billfold
|
5
|
+
|
6
|
+
# ## Billfold::ControllerSupport
|
7
|
+
#
|
8
|
+
# Gets mixed in to `ApplicationController` automatically.
|
9
|
+
module ControllerSupport
|
10
|
+
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
included do
|
14
|
+
helper_method :current_user
|
15
|
+
end
|
16
|
+
|
17
|
+
module InstanceMethods
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
# ### Billfold::ControllerSupport#current_user
|
22
|
+
#
|
23
|
+
# Return the current user, if signed in.
|
24
|
+
def current_user
|
25
|
+
@current_user ||= ::Billfold::user_class.find_by_id(session[:user_id])
|
26
|
+
end
|
27
|
+
|
28
|
+
# ### Billfold::ControllerSupport#current_user=
|
29
|
+
#
|
30
|
+
# Set the signed-in user.
|
31
|
+
def current_user=(user)
|
32
|
+
session[:user_id] = user ? user.id : nil
|
33
|
+
@current_user = user
|
34
|
+
end
|
35
|
+
|
36
|
+
# ### Billfold::ControllerSupport#require_sign_in
|
37
|
+
#
|
38
|
+
# A before filter that requires a user to be signed in. The default
|
39
|
+
# implementation adds a flash message and redirects to /
|
40
|
+
def require_sign_in
|
41
|
+
unless current_user
|
42
|
+
flash['info'] = 'Please sign in'
|
43
|
+
redirect_to root_path
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'billfold'
|
2
|
+
require 'rails'
|
3
|
+
|
4
|
+
module Billfold
|
5
|
+
class Engine < Rails::Engine
|
6
|
+
engine_name :my_rails_engine
|
7
|
+
|
8
|
+
# Config defaults
|
9
|
+
config.mount_at = '/'
|
10
|
+
|
11
|
+
# Check the gem config
|
12
|
+
initializer "check config" do |app|
|
13
|
+
# make sure mount_at ends with trailing slash
|
14
|
+
config.mount_at += '/' unless config.mount_at.last == '/'
|
15
|
+
end
|
16
|
+
|
17
|
+
initializer "static assets" do |app|
|
18
|
+
app.middleware.use ::ActionDispatch::Static, "#{root}/public"
|
19
|
+
end
|
20
|
+
|
21
|
+
ActiveSupport.on_load(:action_controller) do
|
22
|
+
include Billfold::ControllerSupport
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'billfold'
|
2
|
+
|
3
|
+
module Billfold
|
4
|
+
|
5
|
+
# ## Billfold::Identity
|
6
|
+
#
|
7
|
+
# Support for an identity class. If you are using ActiveRecord, you
|
8
|
+
# probably want `Billfold::ActiveRecordIdentity`, which includes everything
|
9
|
+
# in this module.
|
10
|
+
#
|
11
|
+
# Requires the following attributes
|
12
|
+
# * `user` -- the owning `Billfold::User` instance; required
|
13
|
+
# * `provider` -- the OmniAuth provider name (e.g. "twitter"); required
|
14
|
+
# * `value` -- the unique identifier (within the scope of `provider`) of
|
15
|
+
# the identity (e.g. "my_twitter_handle"); required
|
16
|
+
# * `data` -- additional information passed in from OmniAuth under the
|
17
|
+
# `"user_info"` key
|
18
|
+
module Identity
|
19
|
+
|
20
|
+
def self.included(base)
|
21
|
+
base.extend ClassMethods
|
22
|
+
base.send(:include, InstanceMethods)
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
|
27
|
+
# ### Billfold::Identity.with_provider_and_value
|
28
|
+
#
|
29
|
+
# Return the identity with the given `provider` and `value`, or `nil`,
|
30
|
+
# if no such identity exists. Including classes *must* redefine this
|
31
|
+
# method.
|
32
|
+
def with_provider_and_value(provider, value)
|
33
|
+
raise NotImplementedError.new('classes including Billfold::Identity MUST redefine class method with_provider_and_value')
|
34
|
+
end
|
35
|
+
|
36
|
+
# ### Billfold::Identity.update_or_create!
|
37
|
+
#
|
38
|
+
# Updates or creates a `Billfold::Identity`.
|
39
|
+
#
|
40
|
+
# ### Parameters: a single `Hash` with the following keys:
|
41
|
+
# * `:provider` -- the name of an OmniAuth provider (e.g. "twitter")
|
42
|
+
# * `:user` -- if nil, creates a new `User` for the `Identity`
|
43
|
+
# * `:value` -- the unique identifier
|
44
|
+
# * `:data` -- extra data for the Identity
|
45
|
+
#
|
46
|
+
# ### Behavior
|
47
|
+
#
|
48
|
+
# If `:provider` or `:value` is `nil`, this method raises an
|
49
|
+
# `ArgumentError`.
|
50
|
+
#
|
51
|
+
# If `:user` is `nil`, this method creates a new `User` for the `Identity`.
|
52
|
+
#
|
53
|
+
# If `:user` exists and there is no other `Identity` with the
|
54
|
+
# same `:value`, this method adds a new `Identity` to the `User`.
|
55
|
+
#
|
56
|
+
# If there exists another `Identity` with the same `:value` and
|
57
|
+
# that `Identity` is owned by the given `User`, this method updates that
|
58
|
+
# `Identity`.
|
59
|
+
#
|
60
|
+
# If `:user` exists and there exists another `Identity` with
|
61
|
+
# the same `:value` and that `Identity` is owned by a *different* `User`,
|
62
|
+
# this method merges that User into `:user` and updates the `Identity`.
|
63
|
+
def update_or_create!(attributes = {})
|
64
|
+
raise ArgumentError.new("provider must not be blank") if attributes[:provider].blank?
|
65
|
+
raise ArgumentError.new("value must not be blank") if attributes[:value].blank?
|
66
|
+
identity = with_provider_and_value(attributes[:provider], attributes[:value])
|
67
|
+
if identity
|
68
|
+
old_owner, new_owner = identity.user, attributes[:user]
|
69
|
+
transaction do
|
70
|
+
identity.update_attributes!(attributes)
|
71
|
+
old_owner.merge_into!(new_owner) if old_owner != new_owner
|
72
|
+
end
|
73
|
+
else
|
74
|
+
identity = new(attributes)
|
75
|
+
identity.user = attributes[:user] || ::Billfold.user_class.new(:name => identity.name_for_user)
|
76
|
+
identity.save!
|
77
|
+
end
|
78
|
+
identity
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
module InstanceMethods
|
84
|
+
|
85
|
+
# ### Billfold::Identity#name_for_user
|
86
|
+
#
|
87
|
+
# When creating a new user from an identity, this method is used to
|
88
|
+
# generate a display name. By default, it tries to get the user's name
|
89
|
+
# from the OmniAuth data and falls back on using the identity's provider
|
90
|
+
# and value, but subclasses may have something better to do.
|
91
|
+
def name_for_user
|
92
|
+
(data && data['name']) || "#{provider} #{value}"
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|