intermodal 0.0.1 → 0.4.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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/LICENSE +1 -1
- data/README +6 -4
- data/Rakefile +6 -0
- data/intermodal.gemspec +51 -0
- data/lib/generators/intermodal_generator.rb +8 -0
- data/lib/intermodal.rb +122 -0
- data/lib/intermodal/api.rb +246 -0
- data/lib/intermodal/api/configuration.rb +40 -0
- data/lib/intermodal/api/railties.rb +21 -0
- data/lib/intermodal/concerns/acceptors/named_resource.rb +12 -0
- data/lib/intermodal/concerns/acceptors/resource.rb +12 -0
- data/lib/intermodal/concerns/controllers/accountability.rb +17 -0
- data/lib/intermodal/concerns/controllers/anonymous.rb +24 -0
- data/lib/intermodal/concerns/controllers/authenticatable.rb +24 -0
- data/lib/intermodal/concerns/controllers/paginated_collection.rb +25 -0
- data/lib/intermodal/concerns/controllers/presentation.rb +17 -0
- data/lib/intermodal/concerns/controllers/resource.rb +51 -0
- data/lib/intermodal/concerns/controllers/resource_linking.rb +58 -0
- data/lib/intermodal/concerns/let.rb +21 -0
- data/lib/intermodal/concerns/models/access_credential.rb +28 -0
- data/lib/intermodal/concerns/models/account.rb +16 -0
- data/lib/intermodal/concerns/models/accountability.rb +47 -0
- data/lib/intermodal/concerns/models/db_access_token.rb +29 -0
- data/lib/intermodal/concerns/models/has_parent_resource.rb +45 -0
- data/lib/intermodal/concerns/models/presentation.rb +25 -0
- data/lib/intermodal/concerns/models/redis_access_token.rb +60 -0
- data/lib/intermodal/concerns/models/resource_linking.rb +126 -0
- data/lib/intermodal/concerns/models/sanitize_html.rb +35 -0
- data/lib/intermodal/concerns/presenters/named_resource.rb +12 -0
- data/lib/intermodal/concerns/presenters/resource.rb +14 -0
- data/lib/intermodal/concerns/rails/rails_3_stack.rb +42 -0
- data/lib/intermodal/concerns/rails/rails_4_stack.rb +17 -0
- data/lib/intermodal/concerns/rails/use_warden.rb +21 -0
- data/lib/intermodal/config.rb +15 -0
- data/lib/intermodal/configuration.rb +11 -0
- data/lib/intermodal/controllers/api_controller.rb +26 -0
- data/lib/intermodal/controllers/linking_resource_controller.rb +8 -0
- data/lib/intermodal/controllers/nested_resource_controller.rb +18 -0
- data/lib/intermodal/controllers/resource_controller.rb +11 -0
- data/lib/intermodal/dsl/controllers.rb +125 -0
- data/lib/intermodal/dsl/mapping.rb +79 -0
- data/lib/intermodal/dsl/presentation_helpers.rb +107 -0
- data/lib/intermodal/mapping/acceptor.rb +2 -2
- data/lib/intermodal/mapping/mapper.rb +39 -13
- data/lib/intermodal/mapping/presenter.rb +12 -6
- data/lib/intermodal/proxies/linking_resources.rb +58 -0
- data/lib/intermodal/proxies/will_paginate.rb +85 -0
- data/lib/intermodal/rack/auth.rb +29 -0
- data/lib/intermodal/rack/dummy_store.rb +24 -0
- data/lib/intermodal/rack/rescue.rb +82 -0
- data/lib/intermodal/responders/linking_resource_responder.rb +21 -0
- data/lib/intermodal/responders/resource_responder.rb +64 -0
- data/lib/intermodal/rspec/acceptors.rb +79 -0
- data/lib/intermodal/rspec/models/accountability.rb +114 -0
- data/lib/intermodal/rspec/models/has_parent_resource.rb +132 -0
- data/lib/intermodal/rspec/models/resource_linking.rb +234 -0
- data/lib/intermodal/rspec/models/sanitization.rb +84 -0
- data/lib/intermodal/rspec/presenters.rb +92 -0
- data/lib/intermodal/rspec/requests/authenticated_requests.rb +17 -0
- data/lib/intermodal/rspec/requests/linked_resources.rb +180 -0
- data/lib/intermodal/rspec/requests/paginated_collection.rb +60 -0
- data/lib/intermodal/rspec/requests/rack.rb +142 -0
- data/lib/intermodal/rspec/requests/request_validations.rb +36 -0
- data/lib/intermodal/rspec/requests/resources.rb +275 -0
- data/lib/intermodal/rspec/requests/rfc2616_status_codes.rb +51 -0
- data/lib/intermodal/rspec/validators.rb +86 -0
- data/lib/intermodal/validators/account_validator.rb +27 -0
- data/lib/intermodal/validators/different_account_validator.rb +27 -0
- data/lib/intermodal/version.rb +3 -0
- data/spec/mapping/acceptors_spec.rb +142 -0
- data/spec/mapping/presenters_spec.rb +186 -0
- data/spec/models/accountability_spec.rb +13 -0
- data/spec/models/has_parent_resource_spec.rb +18 -0
- data/spec/models/resource_linking_spec.rb +21 -0
- data/spec/proxies/will_paginate_spec.rb +163 -0
- data/spec/rack/auth_spec.rb +51 -0
- data/spec/requests/linked_resources.rb +37 -0
- data/spec/requests/nested_resources_spec.rb +54 -0
- data/spec/requests/resources_spec.rb +50 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/support/api.rb +50 -0
- data/spec/support/app/class_builder.rb +41 -0
- data/spec/support/app/db/adapter_helper.rb +53 -0
- data/spec/support/app/db/authentication_schema_helper.rb +62 -0
- data/spec/support/app/db/migration_helper.rb +44 -0
- data/spec/support/app/schema.rb +101 -0
- data/spec/support/application.rb +23 -0
- data/spec/support/blueprints.rb +41 -0
- data/spec/support/epiphyte.rb +29 -0
- metadata +393 -52
- data/lib/intermodal/base.rb +0 -13
- data/lib/intermodal/declare_controllers.rb +0 -102
- data/lib/intermodal/mapping.rb +0 -4
- data/lib/intermodal/mapping/dsl.rb +0 -76
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'rails/railtie/configuration'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# Modified from Rails 4.2 rails/engine/configuration.rg
|
|
5
|
+
module Intermodal
|
|
6
|
+
class API
|
|
7
|
+
class Configuration < ::Rails::Railtie::Configuration
|
|
8
|
+
attr_writer :middleware
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
super()
|
|
12
|
+
@generators = app_generators.dup
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Returns the middleware stack for the engine.
|
|
16
|
+
def middleware
|
|
17
|
+
@middleware ||= Rails::Configuration::MiddlewareStackProxy.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Holds generators configuration:
|
|
21
|
+
#
|
|
22
|
+
# config.generators do |g|
|
|
23
|
+
# g.orm :data_mapper, migration: true
|
|
24
|
+
# g.template_engine :haml
|
|
25
|
+
# g.test_framework :rspec
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# If you want to disable color in console, do:
|
|
29
|
+
#
|
|
30
|
+
# config.generators.colorize_logging = false
|
|
31
|
+
#
|
|
32
|
+
def generators #:nodoc:
|
|
33
|
+
@generators ||= Rails::Configuration::Generators.new
|
|
34
|
+
yield(@generators) if block_given?
|
|
35
|
+
@generators
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Rails
|
|
2
|
+
class Engine < Railtie
|
|
3
|
+
class Railties
|
|
4
|
+
include Enumerable
|
|
5
|
+
attr_reader :_all
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@_all ||= ::Rails::Railtie.subclasses.map(&:instance) +
|
|
9
|
+
::Rails::Engine.subclasses.map(&:instance)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def each(*args, &block)
|
|
13
|
+
_all.each(*args, &block)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def -(others)
|
|
17
|
+
_all - others
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'intermodal/concerns/controllers/authenticatable'
|
|
2
|
+
|
|
3
|
+
module Intermodal
|
|
4
|
+
module Controllers
|
|
5
|
+
|
|
6
|
+
# This concern builds on top of Intermodal::Controllers::Authenticatable
|
|
7
|
+
# by requiring authentication for all controller actions.
|
|
8
|
+
module Accountability
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
include Intermodal::Controllers::Authenticatable
|
|
13
|
+
before_filter :authentication
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module Controllers
|
|
3
|
+
module Anonymous
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
protected
|
|
7
|
+
|
|
8
|
+
# Hacks to get anonymous controllers working
|
|
9
|
+
def reloadable?(app)
|
|
10
|
+
false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ClassMethods
|
|
14
|
+
def anonymous?
|
|
15
|
+
true
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def controller_path
|
|
19
|
+
'anonymous_web_service'
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'warden'
|
|
2
|
+
|
|
3
|
+
module Intermodal
|
|
4
|
+
module Controllers
|
|
5
|
+
# This concern adds an interface for warden, allowing access to
|
|
6
|
+
# the warden user as well as exposing a method to ask warden
|
|
7
|
+
# for authentication.
|
|
8
|
+
module Authenticatable
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
include Intermodal::Let
|
|
13
|
+
|
|
14
|
+
let(:account) { env['warden'].user }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
protected
|
|
18
|
+
|
|
19
|
+
def authentication
|
|
20
|
+
throw(:warden) unless env['warden'].authenticate?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module Controllers
|
|
3
|
+
module PaginatedCollection
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
let(:collection) { raise 'You must define collection' }
|
|
8
|
+
let(:collection_name) { self.class.collection_name.to_s } # TODO: This might already be defined in Rails 3.x
|
|
9
|
+
let(:presented_collection) { collection.paginate :page => params[:page], :per_page => per_page }
|
|
10
|
+
|
|
11
|
+
let(:per_page) do
|
|
12
|
+
if params[:per_page]
|
|
13
|
+
params[:per_page].to_i <= api.max_per_page ? params[:per_page] : api.max_per_page
|
|
14
|
+
else
|
|
15
|
+
api.default_per_page
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def index
|
|
21
|
+
respond_with presented_collection, :presentation_root => collection_name, :presentation_scope => presentation_scope_for_index
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module Controllers
|
|
3
|
+
module Presentation
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
include Intermodal::Let
|
|
8
|
+
|
|
9
|
+
let(:api) { self.class.api }
|
|
10
|
+
let(:presenter) { api.presenters[model_name] }
|
|
11
|
+
let(:acceptor) { api.acceptors[model_name] }
|
|
12
|
+
let(:accepted_params) { acceptor.call(params || {}) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module Controllers
|
|
3
|
+
module Resource
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
include Intermodal::Controllers::Accountability
|
|
8
|
+
include Intermodal::Controllers::Anonymous
|
|
9
|
+
include Intermodal::Controllers::PaginatedCollection
|
|
10
|
+
|
|
11
|
+
respond_to :json
|
|
12
|
+
|
|
13
|
+
class_attribute :model, :collection_name, :api
|
|
14
|
+
|
|
15
|
+
let(:resource) { raise 'You must define resource' }
|
|
16
|
+
|
|
17
|
+
let(:model) { self.class.model || self.class.collection_name.to_s.classify.constantize }
|
|
18
|
+
let(:resource_name) {collection_name.singularize }
|
|
19
|
+
let(:model_name) { model.name.underscore.to_sym }
|
|
20
|
+
|
|
21
|
+
# Wrap JSON with root key?
|
|
22
|
+
let(:presentation_root) { nil }
|
|
23
|
+
let(:presentation_scope) { nil } # Will default to :default scope
|
|
24
|
+
let(:presentation_scope_for_index) { nil }
|
|
25
|
+
let(:always_nest_collections) { false }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Actions
|
|
29
|
+
|
|
30
|
+
# index defined in PaginatedCollection
|
|
31
|
+
|
|
32
|
+
def show
|
|
33
|
+
respond_with resource
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def create
|
|
37
|
+
respond_with model.create(create_params)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def update
|
|
41
|
+
resource.update_attributes(update_params)
|
|
42
|
+
respond_with resource
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def destroy
|
|
46
|
+
resource.destroy
|
|
47
|
+
respond_with resource
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module Controllers
|
|
3
|
+
module ResourceLinking
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
class_attribute :api
|
|
8
|
+
|
|
9
|
+
include Intermodal::Controllers::Accountability
|
|
10
|
+
include Intermodal::Controllers::Anonymous
|
|
11
|
+
|
|
12
|
+
respond_to :json
|
|
13
|
+
self.responder = ResourceResponder
|
|
14
|
+
|
|
15
|
+
let(:parent_id) { params[:id] }
|
|
16
|
+
let(:collection_name) { self.class.collection_name.to_s } # TODO: This might already be defined in Rails 3.x
|
|
17
|
+
let(:collection) { model.get(:all, :parent_id => parent_id).to_target_ids }
|
|
18
|
+
|
|
19
|
+
# :target_element_name: Params key for list of linked resource ids
|
|
20
|
+
let(:target_element_name) { "#{target_resource_name.to_s.singularize}_ids" }
|
|
21
|
+
|
|
22
|
+
# :accepted_params: All attribute and values for resource
|
|
23
|
+
let(:accepted_params) { params[parent_resource_name] || {} }
|
|
24
|
+
|
|
25
|
+
# :target_ids: List of ids extracted from params
|
|
26
|
+
let(:target_ids) { accepted_params[target_element_name] }
|
|
27
|
+
|
|
28
|
+
let(:presentation_root) { parent_resource_name }
|
|
29
|
+
let(:presentation_scope) { nil } # Will default to :default scope
|
|
30
|
+
let(:presented_collection) do
|
|
31
|
+
Intermodal::Proxies::LinkingResources.new presentation_root,
|
|
32
|
+
:to => target_resource_name,
|
|
33
|
+
:with => collection,
|
|
34
|
+
:parent_id => parent_id
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def index
|
|
39
|
+
respond_with presented_collection
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def create
|
|
43
|
+
return head :status => 422 unless target_ids
|
|
44
|
+
respond_with model.replace(params[:id], target_ids, :account => account), :location => nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def update
|
|
48
|
+
return head :status => 422 unless target_ids
|
|
49
|
+
respond_with(model.append(params[:id], target_ids, :account => account))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def destroy
|
|
53
|
+
return head :status => 422 unless target_ids
|
|
54
|
+
respond_with(model.remove(params[:id], target_ids, :account => account))
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Ripped from Rspec 2.0 beta.12
|
|
2
|
+
module Intermodal
|
|
3
|
+
module Let
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def __memoized # :nodoc:
|
|
9
|
+
@__memoized ||= {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module ClassMethods
|
|
13
|
+
def let(name, &block)
|
|
14
|
+
define_method(name) do
|
|
15
|
+
__memoized[name] ||= instance_eval(&block)
|
|
16
|
+
end
|
|
17
|
+
protected(name)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'active_model/secure_password'
|
|
2
|
+
|
|
3
|
+
module Intermodal
|
|
4
|
+
module Models
|
|
5
|
+
module AccessCredential
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
include Intermodal::Models::Accountability
|
|
10
|
+
include ActiveModel::SecurePassword
|
|
11
|
+
|
|
12
|
+
has_secure_password
|
|
13
|
+
|
|
14
|
+
# Validations
|
|
15
|
+
validates_uniqueness_of :identity
|
|
16
|
+
|
|
17
|
+
# Associations
|
|
18
|
+
belongs_to :account
|
|
19
|
+
|
|
20
|
+
def self.authenticate!(identity, key)
|
|
21
|
+
access = self.find_by(identity: identity).try(:authenticate, key)
|
|
22
|
+
return false unless access
|
|
23
|
+
access.account
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module Models
|
|
3
|
+
module Account
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
# Validations
|
|
8
|
+
validates_presence_of :name
|
|
9
|
+
|
|
10
|
+
# Associations
|
|
11
|
+
has_many :access_tokens
|
|
12
|
+
has_many :access_credentials
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module Models
|
|
3
|
+
module Accountability
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
# TODO: This might not be necessary
|
|
8
|
+
include Intermodal::Models::Presentation
|
|
9
|
+
|
|
10
|
+
# Validations
|
|
11
|
+
validates_presence_of :account_id
|
|
12
|
+
|
|
13
|
+
# Associations
|
|
14
|
+
belongs_to :account
|
|
15
|
+
|
|
16
|
+
# Scopes
|
|
17
|
+
scope :by_account_id, lambda { |a_id| where(:account_id => a_id) }
|
|
18
|
+
scope :by_account, lambda { |a| by_account_id(a.id) }
|
|
19
|
+
|
|
20
|
+
extend Get
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module Get
|
|
24
|
+
# API: overrideable
|
|
25
|
+
# Use this to extend Get
|
|
26
|
+
def build_get_query(_query, opts)
|
|
27
|
+
_query = _query.by_account(opts[:account]) if opts[:account]
|
|
28
|
+
_query = _query.by_account_id(opts[:account_id]) if opts[:account_id]
|
|
29
|
+
return _query
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def get(_id, opts = {})
|
|
33
|
+
# Rails 4 no longer has .scoped, and where(nil) is the actual replacement
|
|
34
|
+
# always returning a relation
|
|
35
|
+
# See: http://stackoverflow.com/a/18199294/313193
|
|
36
|
+
_query = build_get_query(self.where(nil), opts)
|
|
37
|
+
return _query if _id == :all
|
|
38
|
+
|
|
39
|
+
# Force false on readonly
|
|
40
|
+
_resource = _query.where(:id => _id).limit(1).readonly(false).first
|
|
41
|
+
raise ActiveRecord::RecordNotFound, "Could not find resource" unless _resource
|
|
42
|
+
return _resource
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Intermodal
|
|
2
|
+
module Models
|
|
3
|
+
module DbAccessToken
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
TOKEN_SIZE = 32
|
|
8
|
+
|
|
9
|
+
# Validations
|
|
10
|
+
validates_uniqueness_of :token
|
|
11
|
+
|
|
12
|
+
# Associations
|
|
13
|
+
belongs_to :account
|
|
14
|
+
|
|
15
|
+
def self.authenticate!(token)
|
|
16
|
+
::Account.joins(:access_tokens).where(:access_tokens => { :token => token }).limit(1).first
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.generate!(account)
|
|
20
|
+
token = new(:account_id => account.id)
|
|
21
|
+
begin
|
|
22
|
+
token.token = SecureRandom.hex(TOKEN_SIZE)
|
|
23
|
+
end until token.valid?
|
|
24
|
+
return token.token if token.save
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|