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.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +1 -1
  5. data/README +6 -4
  6. data/Rakefile +6 -0
  7. data/intermodal.gemspec +51 -0
  8. data/lib/generators/intermodal_generator.rb +8 -0
  9. data/lib/intermodal.rb +122 -0
  10. data/lib/intermodal/api.rb +246 -0
  11. data/lib/intermodal/api/configuration.rb +40 -0
  12. data/lib/intermodal/api/railties.rb +21 -0
  13. data/lib/intermodal/concerns/acceptors/named_resource.rb +12 -0
  14. data/lib/intermodal/concerns/acceptors/resource.rb +12 -0
  15. data/lib/intermodal/concerns/controllers/accountability.rb +17 -0
  16. data/lib/intermodal/concerns/controllers/anonymous.rb +24 -0
  17. data/lib/intermodal/concerns/controllers/authenticatable.rb +24 -0
  18. data/lib/intermodal/concerns/controllers/paginated_collection.rb +25 -0
  19. data/lib/intermodal/concerns/controllers/presentation.rb +17 -0
  20. data/lib/intermodal/concerns/controllers/resource.rb +51 -0
  21. data/lib/intermodal/concerns/controllers/resource_linking.rb +58 -0
  22. data/lib/intermodal/concerns/let.rb +21 -0
  23. data/lib/intermodal/concerns/models/access_credential.rb +28 -0
  24. data/lib/intermodal/concerns/models/account.rb +16 -0
  25. data/lib/intermodal/concerns/models/accountability.rb +47 -0
  26. data/lib/intermodal/concerns/models/db_access_token.rb +29 -0
  27. data/lib/intermodal/concerns/models/has_parent_resource.rb +45 -0
  28. data/lib/intermodal/concerns/models/presentation.rb +25 -0
  29. data/lib/intermodal/concerns/models/redis_access_token.rb +60 -0
  30. data/lib/intermodal/concerns/models/resource_linking.rb +126 -0
  31. data/lib/intermodal/concerns/models/sanitize_html.rb +35 -0
  32. data/lib/intermodal/concerns/presenters/named_resource.rb +12 -0
  33. data/lib/intermodal/concerns/presenters/resource.rb +14 -0
  34. data/lib/intermodal/concerns/rails/rails_3_stack.rb +42 -0
  35. data/lib/intermodal/concerns/rails/rails_4_stack.rb +17 -0
  36. data/lib/intermodal/concerns/rails/use_warden.rb +21 -0
  37. data/lib/intermodal/config.rb +15 -0
  38. data/lib/intermodal/configuration.rb +11 -0
  39. data/lib/intermodal/controllers/api_controller.rb +26 -0
  40. data/lib/intermodal/controllers/linking_resource_controller.rb +8 -0
  41. data/lib/intermodal/controllers/nested_resource_controller.rb +18 -0
  42. data/lib/intermodal/controllers/resource_controller.rb +11 -0
  43. data/lib/intermodal/dsl/controllers.rb +125 -0
  44. data/lib/intermodal/dsl/mapping.rb +79 -0
  45. data/lib/intermodal/dsl/presentation_helpers.rb +107 -0
  46. data/lib/intermodal/mapping/acceptor.rb +2 -2
  47. data/lib/intermodal/mapping/mapper.rb +39 -13
  48. data/lib/intermodal/mapping/presenter.rb +12 -6
  49. data/lib/intermodal/proxies/linking_resources.rb +58 -0
  50. data/lib/intermodal/proxies/will_paginate.rb +85 -0
  51. data/lib/intermodal/rack/auth.rb +29 -0
  52. data/lib/intermodal/rack/dummy_store.rb +24 -0
  53. data/lib/intermodal/rack/rescue.rb +82 -0
  54. data/lib/intermodal/responders/linking_resource_responder.rb +21 -0
  55. data/lib/intermodal/responders/resource_responder.rb +64 -0
  56. data/lib/intermodal/rspec/acceptors.rb +79 -0
  57. data/lib/intermodal/rspec/models/accountability.rb +114 -0
  58. data/lib/intermodal/rspec/models/has_parent_resource.rb +132 -0
  59. data/lib/intermodal/rspec/models/resource_linking.rb +234 -0
  60. data/lib/intermodal/rspec/models/sanitization.rb +84 -0
  61. data/lib/intermodal/rspec/presenters.rb +92 -0
  62. data/lib/intermodal/rspec/requests/authenticated_requests.rb +17 -0
  63. data/lib/intermodal/rspec/requests/linked_resources.rb +180 -0
  64. data/lib/intermodal/rspec/requests/paginated_collection.rb +60 -0
  65. data/lib/intermodal/rspec/requests/rack.rb +142 -0
  66. data/lib/intermodal/rspec/requests/request_validations.rb +36 -0
  67. data/lib/intermodal/rspec/requests/resources.rb +275 -0
  68. data/lib/intermodal/rspec/requests/rfc2616_status_codes.rb +51 -0
  69. data/lib/intermodal/rspec/validators.rb +86 -0
  70. data/lib/intermodal/validators/account_validator.rb +27 -0
  71. data/lib/intermodal/validators/different_account_validator.rb +27 -0
  72. data/lib/intermodal/version.rb +3 -0
  73. data/spec/mapping/acceptors_spec.rb +142 -0
  74. data/spec/mapping/presenters_spec.rb +186 -0
  75. data/spec/models/accountability_spec.rb +13 -0
  76. data/spec/models/has_parent_resource_spec.rb +18 -0
  77. data/spec/models/resource_linking_spec.rb +21 -0
  78. data/spec/proxies/will_paginate_spec.rb +163 -0
  79. data/spec/rack/auth_spec.rb +51 -0
  80. data/spec/requests/linked_resources.rb +37 -0
  81. data/spec/requests/nested_resources_spec.rb +54 -0
  82. data/spec/requests/resources_spec.rb +50 -0
  83. data/spec/spec_helper.rb +53 -0
  84. data/spec/support/api.rb +50 -0
  85. data/spec/support/app/class_builder.rb +41 -0
  86. data/spec/support/app/db/adapter_helper.rb +53 -0
  87. data/spec/support/app/db/authentication_schema_helper.rb +62 -0
  88. data/spec/support/app/db/migration_helper.rb +44 -0
  89. data/spec/support/app/schema.rb +101 -0
  90. data/spec/support/application.rb +23 -0
  91. data/spec/support/blueprints.rb +41 -0
  92. data/spec/support/epiphyte.rb +29 -0
  93. metadata +393 -52
  94. data/lib/intermodal/base.rb +0 -13
  95. data/lib/intermodal/declare_controllers.rb +0 -102
  96. data/lib/intermodal/mapping.rb +0 -4
  97. 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,12 @@
1
+ module Intermodal
2
+ module Acceptors
3
+ module NamedResource
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include Intermodal::Acceptors::Resource
8
+ accepts :name
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Intermodal
2
+ module Acceptors
3
+ module Resource
4
+ extend ActiveSupport::Concern
5
+
6
+ # Explicit blacklist
7
+ included do
8
+ exclude_from_acceptance :id, :created_at, :updated_at, :account_id
9
+ end
10
+ end
11
+ end
12
+ 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