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,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Intermodal::Rack::Auth do
4
+ include Intermodal::RSpec::Rack
5
+ include SpecHelpers::Application
6
+
7
+ let(:application) { Intermodal::Rack::Auth }
8
+
9
+ context 'when successfully authenticating' do
10
+ let(:http_headers) { {
11
+ 'X-Auth-Identity' => access_credential.identity,
12
+ 'X-Auth-Key' => access_credential.key } }
13
+
14
+ request :get, '/' do
15
+ expects_status(204)
16
+ expects_empty_body # Make sure returned body responds to #each (String has no #each in 1.9)
17
+
18
+ it 'should respond with a valid X-Auth-Token header' do
19
+ response_headers.should be_include('X-Auth-Token')
20
+ AccessToken.authenticate!(response_headers['X-Auth-Token']).should eql(account)
21
+ end
22
+
23
+ end
24
+ end
25
+
26
+ context 'with invalid authentication credentials' do
27
+ let(:http_headers) { {
28
+ 'X-Auth-Identity' => 'no identity',
29
+ 'X-Auth-Key' => 'no key' } }
30
+
31
+ request :get, '/' do
32
+ expects_status(401)
33
+ expects_empty_body # Make sure returned body responds to #each (String has no #each in 1.9)
34
+ end
35
+ end
36
+
37
+ context 'with invalid access token' do
38
+ let(:x_access_token) { 'invalid_key' }
39
+ let(:delete_access_token) { AccessToken::REDIS.del("auth:#{x_access_token}") }
40
+ let(:http_headers) do
41
+ delete_access_token
42
+ { 'X-Access-Token' => x_access_token }
43
+ end
44
+
45
+ request :get, '/' do
46
+ expects_status(401)
47
+ expects_empty_body # Make sure returned body responds to #each (String has no #each in 1.9)
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Intermodal::ResourceController do
4
+ include Intermodal::RSpec::LinkedResources
5
+ include SpecHelpers::Application
6
+ include SpecHelpers::API
7
+
8
+ let(:api_class) do
9
+ define_class :Api, Rails::Engine do
10
+ include Intermodal::API
11
+ include SpecHelpers::Epiphyte
12
+
13
+ controllers do
14
+ link_resources_from :items, :to => :vendors, :model => Sku
15
+ end
16
+
17
+ routes.draw do
18
+ get '/items/:id/vendors(.:format)', :to => 'items/vendors#index'
19
+ post '/items/:id/vendors(.:format)', :to => 'items/vendors#create'
20
+ put '/items/:id/vendors(.:format)', :to => 'items/vendors#update'
21
+ delete '/items/:id/vendors(.:format)', :to => 'items/vendors#destroy'
22
+ end
23
+ end
24
+ end
25
+
26
+ context 'when implemented within an api engine' do
27
+ link_resource :items, :to => :vendors, :with => Sku do
28
+ let(:model_parents) { [ item ] }
29
+ let(:model_collection) { skus; items.skus.to_sku_ids }
30
+ let(:unlinked_targets) { Sku.make!(3, :item => item) }
31
+ let(:skus) { item.vendors << vendors }
32
+ let(:vendors) { Vendor.make!(3, :account => account) }
33
+
34
+ expects_crud_for_linked_resource
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Intermodal::NestedResourceController do
4
+ include Intermodal::RSpec::Resources
5
+ include SpecHelpers::Application
6
+ include SpecHelpers::API
7
+
8
+ let(:api_class) do
9
+ define_class :Api, Rails::Engine do
10
+ include Intermodal::API
11
+ include SpecHelpers::Epiphyte
12
+
13
+ map_data do
14
+ presentation_for :part do
15
+ presents :id
16
+ presents :item_id
17
+ presents :description
18
+ end
19
+
20
+ acceptance_for :part do
21
+ accepts :name
22
+ end
23
+ end
24
+
25
+ controllers do
26
+ nested_resources :items, :parts
27
+ end
28
+
29
+ routes.draw do
30
+ resources :items do
31
+ resources :parts, :controller => 'items/parts'
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ context 'when implemented within an api engine' do
38
+ let(:model_factory_options) { (model_parent ? { parent_names.last.to_s.singularize => model_parent } : {:account => account} ) }
39
+ let(:model_collection) do
40
+ 3.times { model.make!(model_factory_options) }
41
+ (model_parent ? model.by_parent(model_parent) : model.by_account(account) )
42
+ end
43
+
44
+ resources [:items, :parts] do
45
+ let(:model_parents) { [ item ] }
46
+ let(:model) { Part }
47
+
48
+ given_create_attributes :name => 'New Part', :item_id => 1
49
+ given_update_attributes :name => 'Updated Part'
50
+
51
+ expects_resource_crud
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Intermodal::ResourceController do
4
+ include Intermodal::RSpec::Resources
5
+ include SpecHelpers::Application
6
+ include SpecHelpers::API
7
+
8
+ let(:api_class) do
9
+ define_class :Api, Rails::Engine do
10
+ include Intermodal::API
11
+ include SpecHelpers::Epiphyte
12
+
13
+ map_data do
14
+ presentation_for :item do
15
+ presents :id
16
+ presents :name
17
+ presents :description
18
+ end
19
+
20
+ acceptance_for :item do
21
+ accepts :name
22
+ accepts :description
23
+ end
24
+ end
25
+
26
+ controllers do
27
+ resources :items
28
+ end
29
+
30
+ routes.draw do
31
+ resources :items
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'when implemented within an api engine' do
37
+ let(:model_factory_options) { (model_parent ? { parent_names.last.to_s.singularize => model_parent } : {:account => account} ) }
38
+ let(:model_collection) do
39
+ 3.times { model.make!(model_factory_options) }
40
+ (model_parent ? model.by_parent(model_parent) : model.by_account(account) )
41
+ end
42
+
43
+ resources :items do
44
+ given_create_attributes :name => 'New Item', :description => 'Ipsum Lorem', :account_id => 1
45
+ given_update_attributes :name => 'Updated Merchant', :description => 'Lorem Ipsum'
46
+
47
+ expects_resource_crud
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,53 @@
1
+ $LOAD_PATH.unshift '.'
2
+
3
+ require 'intermodal'
4
+ require 'rails'
5
+ require 'active_record'
6
+
7
+ require 'will_paginate/active_record'
8
+
9
+ # TODO: Figure out how to load this automatically through the epiphyte
10
+ require 'intermodal/proxies/will_paginate'
11
+ ::WillPaginate::Collection.send(:include, Intermodal::Proxies::WillPaginate::Collection)
12
+
13
+ require 'action_controller'
14
+ require 'rspec-rails'
15
+ require 'database_cleaner'
16
+ require 'machinist/active_record'
17
+ require 'redis'
18
+ require 'ap' # Debugging
19
+ require 'pry'
20
+
21
+
22
+ # Usage:
23
+ # variable_to_watch.tap(&WATCH)
24
+ WATCH = lambda { |o| ap o } unless defined?(WATCH)
25
+
26
+ # Log to stderr
27
+ Rails.logger = ActiveSupport::BufferedLogger.new($stderr, ActiveSupport::BufferedLogger::Severity::ERROR)
28
+
29
+ # Turn this on for debugging
30
+ ActiveRecord::Migration.verbose = false
31
+
32
+ Dir["spec/support/**/*.rb"].map { |f| f.gsub(%r{.rb$}, '') }.each { |f| require f }
33
+
34
+ RSpec.configure do |config|
35
+ config.mock_with :rspec
36
+ config.filter_run :focus => true
37
+ config.filter_run_excluding :external => true
38
+ config.run_all_when_everything_filtered = true
39
+
40
+ # Uncomment to use with Rspec Rails
41
+ # If you'd prefer not to run each of your examples within a transaction,
42
+ # uncomment the following line.
43
+ #config.use_transactional_examples false
44
+
45
+ config.before(:suite) do
46
+ DatabaseCleaner.strategy = :truncation # :transaction strategy currently does not work
47
+ DatabaseCleaner.clean_with :truncation
48
+ end
49
+
50
+ config.before(:each) { DatabaseCleaner.start }
51
+ config.after(:each) { DatabaseCleaner.clean }
52
+ end
53
+
@@ -0,0 +1,50 @@
1
+ module SpecHelpers
2
+ module API
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ let(:application) { api }
7
+ let(:http_headers) { {'X-Auth-Token' => access_token.token } }
8
+
9
+ let(:api) do
10
+ api_class.tap do |a|
11
+ stub_warden_serializer
12
+ stub_abstract_store
13
+ a.instance.initializers.reject! { |initializer| skipped_initializers.include? initializer.name }
14
+
15
+ a.instance.initialize!
16
+ end
17
+ end
18
+
19
+ # TODO: REFACTOR
20
+ # This has to be done because we're using a custom serializer to Redis
21
+ # The right thing to do is to use Warden's serialization. This allows
22
+ # people to use their own authentication scheme besides the built-in one
23
+ # (and possibly spin off the built-in auth as its own gem).
24
+ let(:stub_warden_serializer) do
25
+ class Warden::SessionSerializer
26
+ def store(user, scope)
27
+ end
28
+
29
+ def fetch(scope)
30
+ end
31
+
32
+ def stored?(scope)
33
+ false
34
+ end
35
+
36
+ def delete(scope, user=nil)
37
+ end
38
+ end
39
+ end
40
+ let(:stub_abstract_store) do
41
+ class ::ActionDispatch::Session::AbstractStore
42
+ def destroy_session(env, sid, options)
43
+ end
44
+ end
45
+ end
46
+
47
+ let(:skipped_initializers) { [:add_routing_paths, :append_assets_path, :prepend_helpers_path] }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,41 @@
1
+ # Taken from Remarkable 4.0.0 alpha
2
+ # https://github.com/remarkable/remarkable/blob/1e653044dfb9726034f600bae13d81e9986c44f6/remarkable_activerecord/spec/support/model_builder.rb
3
+
4
+ require 'machinist/active_record'
5
+
6
+ module SpecHelpers
7
+ module ClassBuilder
8
+ extend ActiveSupport::Concern
9
+
10
+ def unload_class(class_name)
11
+ Object.send(:remove_const, class_name) if Object.const_defined?(class_name)
12
+ end
13
+
14
+ def define_class(class_name, base, &block)
15
+ class_name = class_name.to_s.camelize
16
+
17
+ unload_class(class_name)
18
+
19
+ Class.new(base).tap do |klass|
20
+ Object.const_set(class_name, klass)
21
+ klass.class_eval(&block) if block_given?
22
+ end
23
+ end
24
+
25
+ def define_model_class(class_name, base = nil, &block)
26
+ base = base || ActiveRecord::Base
27
+ klass = define_class(class_name, base) do
28
+ #extend Machinist::Machinable
29
+ #include Machinist::Blueprints
30
+ #include Machinist::ActiveRecordExtensions
31
+
32
+ #def self.blueprint_class
33
+ # Machinist::ActiveRecord::Blueprint
34
+ #end
35
+ end
36
+
37
+ klass.class_eval(&block) if block_given?
38
+ klass
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,53 @@
1
+ module SpecHelpers
2
+ module Adapter
3
+ extend ActiveSupport::Concern
4
+
5
+ CONFIGURATIONS = {
6
+ :postgresql => {
7
+ :adapter => "postgresql",
8
+ :username => "postgres",
9
+ :password => "",
10
+ :database => "test",
11
+ :min_messages => "ERROR" },
12
+ :postgresql_admin => {
13
+ :adapter => "postgresql",
14
+ :username => "postgres",
15
+ :password => "",
16
+ :database => "admin",
17
+ :min_messages => "ERROR" },
18
+ # :postgresql_admin is used to connect in; :postgresql is used to actually test the migrations
19
+ :mysql => {
20
+ :adapter => 'mysql',
21
+ :host => 'localhost',
22
+ :username => 'root',
23
+ :database => 'test' },
24
+ :sqlite3 => {
25
+ :adapter => "sqlite3",
26
+ :database => ":memory:" } }
27
+
28
+ included do
29
+ let(:database_env) { CONFIGURATIONS[database_type] }
30
+ let(:database_name) { database_env[:database] }
31
+ let(:postgres_admin_env) { CONFIGURATIONS[:postgresql_admin] }
32
+
33
+ let(:recreate_database) do
34
+ if database_type.to_s == 'sqlite3'
35
+ ActiveRecord::Base.establish_connection(database_env)
36
+ next
37
+ end
38
+
39
+ ActiveRecord::Base.establish_connection(CONFIGURATIONS[:postgresql_admin]) if database_type.to_s == 'postgresql'
40
+
41
+ ActiveRecord::Base.connection.drop_database(database_name)
42
+ ActiveRecord::Base.connection.create_database(database_name)
43
+
44
+ if database_type.to_s == 'postgresql'
45
+ ActiveRecord::Base.connection.disconnect!
46
+ ActiveRecord::Base.establish_connection(database_env)
47
+ else
48
+ ActiveRecord::Base.connection.reset!
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec/support/app/db/adapter_helper'
2
+ require 'spec/support/app/db/migration_helper'
3
+ require 'spec/support/app/class_builder'
4
+
5
+ module SpecHelpers
6
+ module AuthenticationSchema
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ include SpecHelpers::Adapter
11
+ include SpecHelpers::Migration
12
+ include SpecHelpers::ClassBuilder
13
+
14
+ # Simulates rake db:migrate
15
+ let(:auth_db_migrate) { create_accounts; create_account_credentials }
16
+ let(:db_migrate) { auth_db_migrate }
17
+
18
+ # Simulates rake db:recreate
19
+ let(:db_recreate) { recreate_database; db_migrate }
20
+
21
+ # Schema Migrations
22
+ let(:create_accounts) do
23
+ create_table :accounts do |t|
24
+ t.string :name
25
+ end
26
+ end
27
+
28
+ # Note: Properly, account_id should be a foreign key reference
29
+ # but this is not important for the purpose of testing Intermodal
30
+ let(:create_account_credentials) do
31
+ create_table :access_credentials do |t|
32
+ t.integer :account_id
33
+ t.string :identity
34
+ t.string :key
35
+ end
36
+ end
37
+
38
+ let(:establish_redis_connection) { AccessToken.establish_connection! }
39
+
40
+ # Shims
41
+ let(:shims) { [shim_for_account, shim_for_access_credential, shim_for_access_token] }
42
+
43
+ let(:shim_for_account) do
44
+ define_model_class :Account do
45
+ include Intermodal::Models::Account
46
+ end
47
+ end
48
+
49
+ let(:shim_for_access_credential) do
50
+ define_model_class :AccessCredential do
51
+ include Intermodal::Models::AccessCredential
52
+ end
53
+ end
54
+
55
+ let(:shim_for_access_token) do
56
+ define_class :AccessToken, Object do
57
+ include Intermodal::Models::AccessToken
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end