CSC/ECE 517 Fall 2013/ch1 1w34 fs
Aspect-Oriented Programming (AOP) in Ruby on Rails is a programming paradigm which aims at preserving the modularity of object –oriented programs by separating behaviors that make overall implementation scattered, bulky and tangled. It so happens with huge applications that the object boundaries in the problem domain get obscured with the introduction of more and more aspects as an application gets bigger leading to spaghetti code scenario. AOP thus compliments the object-oriented principles such as DRY by removing excess duplication throughout the program by handling cross-cutting concerns, making the resulting code as highly modular, easy to maintain and test.
By isolating behavior, tight coupling between functionalities handling specific logic, in a program can be prevented. Also these independent aspects such as logging, transactions and security can be applied to other applications as well.
Overview
History
Terminology
Cross Cutting Concerns
Many applications share common functionality in their design and implementation. This shared and common logic spans across many layers and leads to repetition and bloating of the application and thereby termed as crosscutting concerns. The aim of AOP is to centralize these functionalities into modular and independent logical aspects which can be inserted into the application either during compilation or at run time, as per requirement, thereby complimenting the DRY principle. The benefits of adopting such an approach are many. Apart from keeping the application with distinct object boundaries, it modularizes aspects in separate locations which are easy to update and maintain in future as well as reuse in other web applications. Some such aspects known commonly which can be treated in AOP way are:
Authentication authorization caching communication logging validation
This reusing behavior as per outside defined aspects, crosscutting concerns can be effectively dealt with.
Advice
Point-cut
Aspect
AOP Interactions
AOP'istic flavored Rails Framework
someController before_filter :login_required
Ruby in Rails implementation
class ItemsUseCase attr_reader :cart, :logger, :items def initialize(cart = cart.new, logger = Logger.new) @cart = cart @logger = logger @items = [] end
# Logging Aspect for adding items to cart def user_adds(item) items << item cart.add(item, success: self.method(:user_added), failure: self.method(:fails_to_add)) end def user_added(item) logger.info "Item successfully added: #{item.name})" end def fails_to_add(item) logger.error "Failed to add item}" end end
Introducing AOP concept for separating Logging
class ItemsUseCase attr_reader :items def initialize @items = [] end def user_adds(item) items << item end def user_added(item); end def fails_to_add(item); end end
require 'aquarium' class ItemsUseCaseGlue attr_reader :usecase, :cart, :logger include Aquarium::Aspects def initialize(usecase, cart, logger) @usecase = usecase @ cart = cart @logger = logger end def inject! Aspect.new(:after, object: usecase, calls_to: :user_adds) do |af, obj, item| cart.push(item, success: usecase.method(:user_added), failure: usecase.method(:fails_to_add)) end Aspect.new(:after, object: usecase, calls_to: :user_added) do |jp, obj, item| logger.info("Successfully added: #{item.name})") end Aspect.new(:after, object: usecase, calls_to: :fails_to_add) do |af, obj, item| logger.error "Failed to add item" end end end
class Application def initialize @items = ItemsUseCase.new @cart = Cart.new @logger = Logger.new @items_glue = ItemsUseCaseGlue.new(items,@cart, @logger) @items_glue.inject! # logic end end