intermodal 0.0.1 → 0.4.0

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