intermodal 0.0.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|