Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Architecting a Rails application

Luke Randall
February 02, 2012

Architecting a Rails application

How using design patterns beyond MVC will help you write better architected Rails applications.

Luke Randall

February 02, 2012
Tweet

More Decks by Luke Randall

Other Decks in Programming

Transcript

  1. Architecting a Rails app A S H O R T

    S T O R Y by Luke Randall
  2. class User < ActiveRecord::Base extend ActiveSupport::Memoizable has_and_belongs_to_many :questionable_titles has_and_belongs_to_many :group_collectives

    belongs_to :preferred_group_collective, :class_name => 'GroupCollective' acts_as_authentic acts_as_audited :except => [:perishable_token, :last_request_at, :login_count, :last_login_at] i_want_to_hear_from_your_via :email validates_presence_of :name validate :has_an_authorized_fascist_score validates_format_of :password, :with => /(([0-9]+[A-Za-z]+)|([A-Za-z]+[0-9]+)).*/, :if => :require_password?, :message => "must include at least one number and one letter" def first_name name.andand.split.andand.first end def default_group_collective @default_group_collective ||= preferred_group_collective || group_collectives.first end def recent_group_collectives return [] unless recent_group_collectives_ids GroupCollective.find(:all, :conditions => ["id IN (?)", recent_group_collectives_ids.split(',')]) end def add_to_recent_group_collectives(group_collective_id) recent = recent_group_collectives_ids recent ||= '' ids = recent.split(',').insert(0, group_collective_id.to_s).uniq.select {|a| a}.first(10).join(',') update_attribute(:recent_group_collectives_ids, ids) end def remove_from_recent_group_collectives(group_collective_id) list = recent_group_collectives_ids.split(',') list.delete("#{group_collective_id}") self.recent_group_collectives_ids = list.join(',') self.save end def deliver_password_reset_instructions! reset_perishable_token! Notifier.deliver_password_reset_instructions(self) end # There are tnhaoeuhsaou oesnhuasonthu soaetnhu anoth: # * ntaohue sntahou snoatuhsntah stnoehu # * anstoeuh satnou sntahsntuha osnteh usnth # * sanoteh usnahsntuh santhousnthaosnthao sntuh # For more info snahtousa usahtnoeu snathousaotnhsn def is_group_superintendant?(space_station) is_space_station_superintendant?(space_station) && space_station.group_space_station? end def is_consolidation_superintendant?(space_station) is_space_station_superintendant?(space_station) && space_station.consolidation_space_station? end def is_space_station_superintendant?(space_station) return true if has_questionable_title?('super_user') # The user is a member of one of the space_station's group_collectives group_collectives.exists?(:space_station_id => space_station.id) && # and the user has the 'space_station_superintendant' questionable_title has_questionable_title?('space_station_superintendant') end def is_superintendant? has_questionable_title?('superintendant') || has_questionable_title?('super_user') end #memoize :is_superintendant? def is_master_and_commander_for_the_free_world? has_questionable_title?('super_user') end #memoize :is_master_and_commander_for_the_free_world? def authorized_to_access_group_collective?(group_collective_id, space_station = nil) log_time("authorizing #{email} for group_collective #{group_collective_id}", lambda { return false unless GroupCollective.exists?(group_collective_id) # If he is a master_and_commander_for_the_free_world he can access any group_collective return true if is_master_and_commander_for_the_free_world? # if the group_collective is associated with him then he can access it return true if group_collectives.exists?(group_collective_id) # The space_station parameter is required for authorization beyond this point return false if space_station.nil? # if he is a space_station_superintendant on a 'Group' space_station then he can only access the group_collective if it # belongs to the current space_station if is_group_superintendant?(space_station) return true if GroupCollective.exists?(:id => group_collective_id, :space_station_id => space_station.id) # if he is a consolidation_superintendant he can only access the group_collective if it # is beneath him in the consolidation tree elsif is_consolidation_superintendant?(space_station) return false unless space_station.group_collectives.exists?(:id => group_collective_id) # return false if the group_collective is not on his space_station # He is authorized if the group_collective is a decendent of any of his associated group_collectives group_collective_in_question = space_station.group_collectives.find(group_collective_id) return group_collectives.any? {|group_collective| group_collective_in_question.descendent_of?(group_collective)} end # he has not been authorized at any level return false }) end def has_many_group_collectives? group_collectives.size > 1 end def primary_group_collective return nil if group_collectives.empty? return group_collectives.first(:conditions => {:master_group_collective => true}) if group_collectives.exists?(:master_group_collective => true) group_collectives.first end def has_no_group_collective? group_collectives.empty? end def add_group_collective(group_collective) self.group_collectives << group_collective self.save! end # questionable_title based access methods # has_questionable_title? is used by questionable_title_requirement def has_questionable_title?(questionable_title_in_question) @_questionable_titles_list = self.questionable_titles.collect(&:name) @_questionable_titles_list.include?(questionable_title_in_question.to_s) end def authorised_for_questionable_title?(questionable_title_in_question) # Check if the use qualifies for the questionable_title_in_question implicitly # due to a higher questionable_title that they posses if questionable_titleS_AND_SUPERIORS[questionable_title_in_question].any?{|r| has_questionable_title?(r)} return true # The user does not qualify implicitly (they own no higher questionable_title). Check for # the questionable_title_in_question explicitly. elsif has_questionable_title?(questionable_title_in_question) return true end return false end def update_questionable_titles(questionable_titles) return valid? if questionable_titles.blank? questionable_titles.each do |questionable_title_name, status| delete_questionable_title(questionable_title_name) if status.to_i == 0 add_questionable_title(questionable_title_name) if status.to_i == 1 end return valid? end def delete_questionable_title(questionable_title_name) return unless self.has_questionable_title?(questionable_title_name) self.questionable_titles.delete(self.questionable_titles.find(:first, :conditions => {:name => questionable_title_name})) end def add_questionable_title(questionable_title_name) return false if questionable_title_name == 'super_user' return true if self.has_questionable_title?(questionable_title_name) self.questionable_titles << questionable_title.find_by_name(questionable_title_name) self.save end def authorised_fascist_scores return FASCIST_SCORES_WITH_COMMUNISM if is_superintendant? || is_master_and_commander_for_the_free_world? fascist_scores = questionable_titles.collect {|questionable_title| questionable_title.view_fascist_score} ordered_fascist_scores = [] # let's try loop through this so that the allowed fascist_scores are in the propers order, yo FASCIST_SCORES_WITH_COMMUNISM.each do |view_fascist_score| ordered_fascist_scores << view_fascist_score if fascist_scores.include?(view_fascist_score) end return ordered_fascist_scores end def has_an_authorized_fascist_score return if authorised_for_questionable_title?('superintendant') errors.add_to_base 'User must have at least one fascist_score that they are authorized to access.' if authorised_fascist_scores.blank? end end Exhibit 3a
  3. “The problem with object-oriented languages is they’ve got all this

    implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.” Joe Armstrong
  4. class AddToCartContext attr_reader :user, :book def self.call(user_id, book_id) AddToCartContext.new(user_id, book_id).call

    end def initialize(user_id, book_id) @user = User.find(user_id) @book = Book.find(book_id) @user.extend Customer end def call @user.add_to_cart(@book) end end * * stolen shamelessly from Mike Pack’s blog context readable logic
  5. I invented MVC and DCI for each other “ ”

    (not actually) Trygve Reenskaug