Migrating to Otto v2.0.0-pre1
What’s New in v2.0.0-pre1
This pre-release includes extensive test coverage improvements (76 new test cases), core module refactoring, middleware stack unification, and a major update to Logic class authentication patterns. See the full changelog for complete details.
The main breaking changes affect: 1. Applications that directly manipulate the middleware stack 2. Logic classes using the old authentication signature
Middleware Stack Unified API
Otto v2.0.0-pre1 introduces a significant refactoring of the middleware stack management, providing a more consistent and efficient approach to middleware configuration.
Key Changes
Unified Middleware Registration
Previous versions had separate legacy and new middleware stacks. In v2.0.0-pre1, we’ve consolidated these into a single, more powerful middleware stack.
Before:
ruby
# Old approach with separate stacks
otto.middleware_stack << SomeMiddleware
otto.middleware.add(AnotherMiddleware)
After:
ruby
# Unified middleware registration
otto.use(SomeMiddleware)
otto.use(AnotherMiddleware)
Performance Improvements
- Middleware lookup is now O(1) using a Set
- Memoized middleware list reduces repeated array creation
- Prevent duplicate middleware registrations with identical configurations
Migration Steps
- Replace all
otto.middleware_stack <<calls withotto.use() - Remove any direct references to
otto.middleware_stack - Use
otto.middleware.includes?()instead of manual stack checks
Authentication and Security Methods
Authentication and security methods now consistently use the new unified middleware stack:
```ruby # Before otto.middleware_stack « Otto::Security::CSRFMiddleware
After (no change needed)
otto.enable_csrf_protection! ```
Performance Considerations
The new implementation is more memory-efficient and provides faster middleware lookups, especially for applications with many middleware components.
Potential Breaking Changes
- Code relying on direct manipulation of
middleware_stackwill need updates - Method signatures for middleware configuration remain the same
Example Migration
```ruby # Old code class MyApp def initialize @otto = Otto.new @otto.middleware_stack « CustomMiddleware @otto.middleware.add(AnotherMiddleware) end end
New code
class MyApp def initialize @otto = Otto.new @otto.use(CustomMiddleware) @otto.use(AnotherMiddleware) end end ```
Troubleshooting
If you encounter any issues with middleware registration or configuration, please file an issue at Otto GitHub Repository.
Logic Class RequestContext Pattern
Otto v2.0.0-pre1 introduces a major improvement to Logic class authentication with the new RequestContext pattern.
Key Changes
New Constructor Signature
Logic classes now use a cleaner, more powerful constructor signature that provides immutable context.
Before: ```ruby class MyLogic attr_reader :session, :user, :params, :locale
def initialize(session, user, params, locale) @session = session @user = user @params = params @locale = locale end
def process return { error: ‘not authenticated’ } unless @user { result: ‘success’, user_name: @user[‘name’] } end end ```
After: ```ruby class MyLogic attr_reader :context, :params, :locale
def initialize(context, params, locale) @context = context @params = params @locale = locale end
def process return { error: ‘not authenticated’ } unless @context.authenticated? { result: ‘success’, user_name: @context.user_name, roles: @context.roles, permissions: @context.permissions } end end ```
RequestContext Benefits
- Immutable Structure - RequestContext is a Ruby Data class that can’t be accidentally modified
- Helper Methods - Built-in methods like
authenticated?,has_role?,has_permission? - Cleaner Interface - Single context parameter instead of separate session/user parameters
- Type Safety - Better IDE support and documentation
- Future-proof - New authentication data automatically available
RequestContext API
```ruby # Authentication status context.authenticated? # Boolean: has non-empty user data context.anonymous? # Boolean: not authenticated
User information
context.user_id # User ID from various possible locations context.user_name # User name/username context.session_id # Session identifier
Role and permission checks
context.has_role?(‘admin’) # Single role check context.has_permission?(‘write’) # Single permission check context.has_any_role?(‘admin’, ‘mod’) # Multiple role check context.roles # Array of all user roles context.permissions # Array of all user permissions
Raw data access
context.session # Session hash context.user # User hash context.auth_method # Authentication method used context.metadata # Additional context data ```
Migration Steps
-
Update Logic class constructor signatures from 4 parameters to 3: ```ruby # Change this: def initialize(session, user, params, locale)
To this:
def initialize(context, params, locale) ```
-
Update instance variables: ```ruby # Change this: @session = session @user = user
To this:
@context = context ```
-
Update authentication checks: ```ruby # Change this: return error unless @user
To this:
return error unless @context.authenticated? ```
-
Update data access: ```ruby # Change this: user_name = @user&.dig(‘name’) user_role = @user&.dig(‘role’)
To this:
user_name = @context.user_name user_role = @context.roles.first ```
Example Migration
Before (v1.x): ```ruby class AdminPanel attr_reader :session, :user, :params, :locale
def initialize(session, user, params, locale) @session = session @user = user @params = params @locale = locale end
def raise_concerns raise ‘Access denied’ unless @user&.dig(‘role’) == ‘admin’ end
def process { panel: ‘admin’, user: @user&.dig(‘name’) || ‘unknown’, session_id: @session&.dig(‘id’) } end end ```
After (v2.0): ```ruby class AdminPanel attr_reader :context, :params, :locale
def initialize(context, params, locale) @context = context @params = params @locale = locale end
def raise_concerns raise ‘Access denied’ unless @context.has_role?(‘admin’) end
def process { panel: ‘admin’, user: @context.user_name || ‘unknown’, session_id: @context.session_id, authenticated: @context.authenticated?, permissions: @context.permissions } end end ```
This provides a cleaner, more maintainable interface while giving Logic classes access to rich authentication context.