SlideShare a Scribd company logo
Michael Mahlberg, Consulting Guild AG
Jens-Christian Fischer, InVisible GmbH

SOLID Ruby -
SOLID Rails
Establishing a sustainable
codebase




                                         1
Who?

       Michael Mahlberg




                          2
Founder of

     roughly a dozen companies
     over the last two decades



                                 3
>> relevance
=> nil




               4
Working as

     A consultant on software
     processes, architecture &
     design for > 2 decades


                                 5
>> relevance != nil
=> true




                      6
Who?

       Jens-Christian Fischer




                                7
Tinkerer, Practician,
      Author
            and generally
          interested in way
           too many things


                              8
What is
SOLID?

          9
SOLID
  is
 not
  a
 Law

        10
PPP
(by Robert C. Martin)




                        Agile Software
                        Development,
                        Principles, Patterns,
                        and Practices




                                                11
Principles!

        You know - more
         like guidelines




                           12
SOLID

SRP OCP LSP ISP DIP

                      13
S OL I D

SRP OCP LSP ISP DIP

                      14
SRP
      Single
      Responsibility
      Principle


                   A class should have
                   one, and only one,
                   reason to change.




                                         15
require	
  'digest/sha1'

class	
  User	
  <	
  ActiveRecord::Base
	
  	
  include	
  Authentication
                                         User Class
	
  	
  include	
  Authentication::ByPassword
	
  	
  include	
  Authentication::ByCookieToken

	
  	
  #TODO	
  Check	
  login	
  redirect	
  if	
  this	
  filter	
  is	
  skipped
	
  	
  #skip_after_filter	
  :store_location

	
  	
  #	
  Virtual	
  attribute	
  for	
  the	
  unencrypted	
  password
	
  	
  attr_accessor	
  :password

	
  	
  belongs_to	
  :country

	
  	
  has_one	
  :user_profile,	
  :dependent	
  =>	
  :destroy
	
  	
  has_one	
  :note
	
  	
  	
  	
  
	
  	
  has_many	
  :queries
	
  	
  has_many	
  :tags,	
  :foreign_key	
  =>	
  "created_by"
	
  	
  has_many	
  :taggings,	
  :as	
  =>	
  :tagger
	
  	
  has_many	
  :organizations,	
  :through	
  =>	
  :affiliations
	
  	
  has_many	
  :affiliations
	
  	
  has_many	
  :locations,	
  :through	
  =>	
  :affiliations
	
  	
  has_many	
  :projects,	
  :through	
  =>	
  :memberships
	
  	
  has_many	
  :memberships
	
  	
  has_many	
  :public_assets,	
  :through	
  =>	
  :privileges
	
  	
  has_many	
  :descriptions,	
  :through	
  =>	
  :privileges
	
  	
  has_many	
  :assessments,	
  :through	
  =>	
  :privileges
	
  	
  has_many	
  :description_profiles,	
  :through	
  =>	
  :privileges
	
  	
  has_many	
  :privileges
	
  	
  has_many	
  :diaries
	
  	
  has_many	
  :roles,	
  :through	
  =>	
  :commitments
	
  	
  has_many	
  :commitments
	
  	
  has_many	
  :activities
	
  	
  has_many	
  :messages
	
  	
  has_many	
  :fellowships
	
  	
  has_many	
  :user_groups,	
  :through	
  =>	
  :fellowships
	
  	
  has_many	
  :survey_responses	
  	
  	
                                        16
So what‘s wrong with
        this?


                       17
From: user.rb
class User < ActiveRecord::Base
  include Authentication
  include Authentication::ByPassword
  include Authentication::ByCookieToken
...
 belongs_to :country
...
  has_one :user_profile, :dependent => :destroy
 has_many :queries
  has_many :tags, :foreign_key => "created_by"
...
  validates_presence_of     :login, :email, :country_id
  validates_presence_of     :password, :if => :password_required?
...




                                                                    18
From: user.rb
  acts_as_state_machine :initial => :pending

  state :pending, :enter => :make_activation_code
  state :active, :enter => :do_activate
...
  event :register do
    transitions :from => :passive, :to => :pending, :guard =>
Proc.new {|u| !(u.crypted_password.blank? &&
u.password.blank?) }
  end
...
  def message_threads
    self.message_threads + self.message_threads
  end




                                                                19
From: user.rb
  def forum_nickname
    self.user_profile.nickname.blank? ? "#{self.first_name} #
{self.last_name}" : self.user_profile.nickname
  end

  def name
    "#{self.first_name} #{self.last_name}" rescue 'n/a'
  end

  def email_with_name
    "#{self.first_name} #{self.last_name} <#{self.email}>"
  end




                                                                20
From: user.rb
def is_admin?
  self.roles.collect{|role| role.title}.include?('admin')
end

def countries
  [self.country]
end




                                                            21
From: user.rb
 def boards
    Board.all :conditions => { :user_group_id =>
self.user_groups.collect{ |g| g.id }}
  end

  def discussions
    Discussion.all :conditions => { :board_id =>
self.boards.collect{ |b| b.id }}
  end

  def organization_roles
    role_ids = Affiliation.all(:conditions => {:user_id =>
self.id}).collect{|a| a.role_id}.uniq
    roles = Role.find(role_ids)
  end




                                                             22
From: user.rb
  def make_password_reset_code
    self.password_reset_code = Digest::SHA1.hexdigest
( Time.now.to_s.split(//).sort_by {rand}.join )
  end

  def self.published_users
    User.all(:conditions => ['state = ?',
'published'], :order => 'login ASC', :include =>
[:user_profile])
  end




                                                        23
Anyone notice a pattern?



                           24
Neither do we



                25
Separation of Concerns



                         26
Authentication
    Roles
   Mailers
    State
     ...

                 27
So how?


  Mixins




           28
New User Model
class User < ActiveRecord::Base
  include Authentication
  include Authentication::ByPassword
  include Authentication::ByCookieToken

  include   Project::UserStates
  include   Project::UserMailer
  include   Project::UserForum
  include   Project::UserMessages
...
end




                                          29
UserMessages
module Project
  module UserMessages
    # to be included in User Model

    has_many :messages
    def message_threads
      MessageThread.all(:conditions =>
        ["sender_id = ? or receiver_id = ?",
          self.id, self.id])
  end
end
end




                                               30
Methods



          31
def transfer(data, url)
  h = Net::HTTP.new(self.uri.host, self.uri.port)
  RAILS_DEFAULT_LOGGER.debug "connecting to CL: #{self.uri}"
  RAILS_DEFAULT_LOGGER.debug "connecting to CL: #{url}"

  resp = h.post(url, data, {'Content-Type' => 'application/xml'})
  response_code = resp.code.to_i
  location = if response_code == 201
    resp['Location']
  else
    RAILS_DEFAULT_LOGGER.debug "error from CL: #{response_code}"
    RAILS_DEFAULT_LOGGER.debug "error from CL: #{resp.body}"
    @error = resp.body
    nil
  end
  [response_code, location]
end




                                                                    32
def transfer(data, document)

  if document.cl_document_url != nil
    self.uri = URI.parse(document.cl_document_url )
    h = Net::HTTP.new(self.uri.host, self.uri.port)
    response = h.post(self.uri, data, {'Content-Type' =>
'application/xml'})
  else
    h = Net::HTTP.new(self.uri.host, self.uri.port)
    response = h.post("/tasks", data, {'Content-Type' =>
'application/xml'})
  end
  response_code = response.code.to_i
  if response_code == 201
    location = response['Location']
    document.cl_document_url = location
    document.save!
  else
    nil
  end
  [response_code, location]
end



                                                           33
SRP Transfer
def transfer data
  open_connection
  post data
  return location
end

def open_connection
  @http = Net::HTTP.new(self.uri.host, self.uri.port)
end

def post data
  @response = http.post(self.url, data, {'Content-Type' =>
                                         'application/xml'})
end




                                                               34
def location
  get_location if created? # returns nil if not created?
end

def response_code
  @response.code.to_i
end

def created?
  response_code == 201
end

def get_location
  @response['Location']
end

def error
  @response.body
end




                                                           35
Add a 16-band
 equalizer & a
   BlueRay
player to this...




                    36
And now to
  this...




             37
S OL I D

SRP OCP LSP ISP DIP

                      38
OCP
  Open
  Closed
  Principle


              You should be able
              to extend a classes
              behavior, without
              modifying it.



                                    39
40
41
42
43
def makemove(map)
                                     From the Google
  x, y = map.my_position
  # calculate a move ...               AI Challenge
  if(valid_moves.size == 0)
    map.make_move( :NORTH )
                                        (Tronbot)
  else
    # choose move ...
    puts move # debug (like in the old days)
    map.make_move( move )
  end
end

class Map
  ...
  def make_move(direction)
    $stdout << ({:NORTH=>1, :SOUTH=>3, :EAST=>2, :WEST=>4}[direction])
    $stdout << "n"
    $stdout.flush
  end
end



                                                                         44
From the Google AI Challenge (Tronbot)
def puts(*args)
  $stderr.puts *args
end

def p(*args)
  args.map!{|arg| arg.inspect}
  puts args
end

def print(*args)
  $stderr.print *args
end




                                         45
Design Sketch




                46
class Outputter

  def initialize(io = $stderr)
    @io = io
  end

  def puts(*args)
    @io.puts *args
  end

  ...
end

out = Outputter.new
out.puts "Testing"




                                 47
S OL I D

SRP OCP LSP ISP DIP

                      48
LSP
      Liskov
      Substitution
      Principle


                     Derived classes
                     must be substitutable
                     for their base
                     classes.



                                             49
No Problem
  in Ruby

        Or so it seems...




                            50
No Interface...

            no problem?




                          51
Wrong !



          52
The classic violation



                        53
A square is a rectangle



                          54
Rectangle

setX
setY




        Square

setX
setY




                   55
Rectange
>>   class Rectangle
>>     attr_accessor :width, :height
>>   end
=>   nil
>>
?>   shape = Rectangle.new
=>   #<Rectangle:0x10114fad0>
>>   shape.width
=>   nil
>>   shape.width=3
>>   shape.width
=>   3
>>   shape.height=5
>>   shape.height
=>   5
>>   shape.width
=>   3



                                       56
Square
>> class Square
?>   def width
>>     @dimension
                            ?> shape = Square.new
>>   end
                            => #<Square:0x101107e88>
?>   def height
                            ?> puts shape.width
>>     @dimension
                            nil
>>   end
                            ?> shape.width=3
?>   def width= n
                            => 3
>>     @dimension = n
                            ?> shape.width
>>   end
                            => 3
?>   def height= n
                            ?> shape.height
>>     @dimension = n
                            => 3
>>   end
>> end




                                                       57
A Problem...
>>   s = [Rectangle.new, Square.new]
=>   [#<Rectangle:0x1005642e8>, #<Square:0x100564298>]
>>   a_rectangle = s[rand(2)]
=>   #<Square:0x100564298>
>>   a_rectangle.height=1
=>   1
>>   a_rectangle.width=3
=>   3
                                Text
>>   a_rectangle.height
=>   3




                                                         58
CCD Common Conceptual
     Denominator


                    59
dup



      60
irb 1:0> 5.respond_to? :dup
=> true
irb 2:0> 5.dup
TypeError: can't dup Fixnum
         from (irb):1:in `dup'
         from (irb):1
irb 3:0>




           http://blog.objectmentor.com/articles/2007/02/17/
           liskov-substitution-principle-and-the-ruby-core-libraries



                                                                  61
S OL I D

SRP OCP LSP ISP DIP

                      62
ISP
      Interface
      Segregation
      Principle


                    Make fine grained
                    interfaces that are
                    client specific.




                                          63
64
Users Controller
class UsersController < ApplicationController

  ssl_required :new, :create, :edit, :update, :destroy, :activate,
:change_passwort, :forgot_password, :reset_password, :make_profile,
:my_contacts
  ssl_allowed :eula, :index, :show

  access_control
[:suspend, :unsuspend, :destroy, :purge, :delete, :admin, :ban, :remove_ban] =>
'admin'

  before_filter :find_user

  skip_after_filter :store_location

  def show
    unless @user == current_user
      redirect_to access_denied_path(@locale)
    else
      respond_to do |format|
         format.html
         format.js { render :partial => "users/#{@context.title}/#{@partial}" }
      end
    end
  end
...


                                                                                  65
more UsersController
def activate
  logout_keeping_session!
  user = User.find_by_activation_code(params[:activation_code]) unless
                          params[:activation_code].blank?

  case
  when (!params[:activation_code].blank?) && user && !user.active?
    user.activate!
    flash[:notice] = t(:message_sign_up_complete)
    unless params[:context].blank?
       redirect_to login_path(:context => params[:context])
    else
       redirect_to "/login"
    end
  when params[:activation_code].blank?
    flash[:error] = t(:message_activation_code_missing)
    redirect_back_or_default("/")
  else
    flash[:error] = t(:message_user_with_that_activation_code_missing)
    redirect_back_or_default("/")
  end
end



                                                                         66
User Class Revisited
class User < ActiveRecord::Base
  ...
end



class Registration < ActiveRecord::Base
   set_table_name "users"

      acts_as_state_machine :initial => :pending

      state :pending, :enter => :make_activation_code
      state :active, :enter => :do_activate
      ...

      event :activate do
        transitions :from => :pending, :to => :active
      end
      ...
end




                                                        67
class RegistrationController < ApplicationController
  ...
  def activate
    logout_keeping_session!
    code_is_blank = params[:activation_code].blank?
    registration = Registration.find_by_activation_code(params
[:activation_code]) unless code_is_blank

    case
    when (!code_is_blank) && registration && !registratio.active?
      registration.activate!
      flash[:notice] = t(:message_sign_up_complete)
      unless params[:context].blank?
         redirect_to login_path(:context => params[:context])
      else
         redirect_to "/login"
      end
    when code_is_blank
      flash[:error] = t(:message_activation_code_missing)
      redirect_back_or_default("/")
    else
      flash[:error] = t(:message_user_with_that_activation_code_missing)
      redirect_back_or_default("/")
    end
  end
  ...
end

                                                                           68
S OL I D

SRP OCP LSP ISP DIP

                      69
DIP
      Dependency
      Inversion
      Principle


                   Depend on
                   abstractions, not on
                   concretions.




                                          70
71
From our OCP example to DIP



out = Outputter.new
out.puts "Testing"




                               72
The code we wish we had
class TronBot
  def initialize
    @@out = TRON_ENVIRONMENT[:debugger]
  end

  def some_method
    ...
    @@out.puts "Testing"
    ...
  end

end




                                          73
TSTTCPW


TRON_ENVIRONMENT = {
        :debugger => Outputter.new ($stderr),
        :game_engine => Outputter.new ($stdout),
        :user_io => Outputter.new ($stderr)
        }




                                                   74
Later...


TRON_ENVIRONMENT = {
        :debugger => Outputter.new ($stderr),
        :game_engine => Outputter.new (TCP_OUTPUTTER),
        :user_io => Outputter.new ($stderr)
        }




                                                         75
DIP Violation in Controller
format.js do
  render :update do |page|
    if @parent_object.class == EspGoal
      @esp_goal_descriptor = @current_object
      page.replace_html "descriptor_#{@current_object.id}",
          :partial => "edit_esp_goal_descriptor",
          :locals => {:esp_goal_descriptor => @esp_goal_descriptor,
                      :parent_object => @parent_object}
    else
      @goal_descriptor = @current_object
      page.replace_html "descriptor_#{@current_object.id}",
          :partial => "edit_goal_descriptor",
          :locals => {:goal_descriptor => @goal_descriptor,
                      :parent_object => @parent_object}
    end
  end
end




                                                                      76
DIP Violation in Controller
format.js do
  render :update do |page|
    if @parent_object.class == EspGoal
      @esp_goal_descriptor = @current_object
      page.replace_html "descriptor_#{@current_object.id}",
          :partial => "edit_esp_goal_descriptor",
          :locals => {:esp_goal_descriptor => @esp_goal_descriptor,
                      :parent_object => @parent_object}
    else if @parent_object.class == Goal
      @goal_descriptor = @current_object
      page.replace_html "descriptor_#{@current_object.id}",
          :partial => "edit_goal_descriptor",
          :locals => {:goal_descriptor => @goal_descriptor,
                      :parent_object => @parent_object}
    else if @parent_object.class == LearningGoal
      ...
      ...
    end
  end
end


                                                                      77
78
1st Refactoring
def show
  ...
  format.js do
    render :update do |page|
      page.replace_html "descriptor_#{@current_object.id}",
                        @parent_object.page_replacement(@current_object)
    end
  end
end

class EspGoal
  def page_replacement child
      { :partial => "edit_esp_goal_descriptor",
        :locals => {:esp_goal_descriptor => child,
                    :parent_object => self}
      }
  end
end

class Goal
  def page_replacement child
    { :partial => "edit_goal_descriptor",
      :locals => {:goal_descriptor => child,
                  :parent_object => self}
    }
  end
end

                                                                           79
80
2nd Refactoring
                                             (wiring)
class PartialContainer
  def add class_symbol, partial_replacement
    @@partinal_replacements.add( class_symbol => partial_replacement)
  end

  def self.partial_replacement an_object
    unless @@partial_replacments
      self.add( EspGoalReplacement.my_class_sym, EspGoalReplacment.new)
      self.add( GoalReplacement.my_class_sym, GoalReplacment.new)
    end
    @@partial_replacement[an_object.class]
  end
end




                                                                          81
class EspGoalReplacmenent
                                                   2nd Refactoring
  def self.my_class_sym

  end
      EspGoal.to_sym                               (Behaviour)
  def partial_definition child
  { :partial => "edit_esp_goal_descriptor",
       :locals => {:esp_goal_descriptor => child,
                   :parent_object => child.esp_goal}
    }
  end
end

class GoalReplacmenent
  def self.my_class_sym
      Goal.to_sym
  end
  def partial_definition child
  { :partial => "edit_goal_descriptor",
       :locals => {:goal_descriptor => child,
                   :parent_object => child.goal}
    }
  end
end



                                                                 82
DIP Violation in Controller
format.js do
  render :update do |page|
    if @parent_object.class == EspGoal
      @esp_goal_descriptor = @current_object
      page.replace_html "descriptor_#{@current_object.id}",
:partial => "edit_esp_goal_descriptor",
          :locals => {:esp_goal_descriptor => @esp_goal_descriptor,
                      :parent_object => @parent_object}
    else
      @goal_descriptor = @current_object
      page.replace_html "descriptor_#{@current_object.id}",
:partial => "edit_goal_descriptor",
          :locals => {:goal_descriptor => @goal_descriptor,
          :parent_object => @parent_object}
    end
  end
end



                                                                      83
2nd Refactoring
                   - the Controller -
def show
  ...
  format.js do
    render :update do |page|
      page.replace_html "descriptor_#{@current_object.id}",
                        PartialContainer.partial_replacement(@parent_object).
                                         partial_definition(@current_object)
    end
  end
end




                                                                                84
85
SOLID

SRP OCP LSP ISP DIP

                      86
SRP OCP LSP ISP DIP

                      87
Questions?
      S OL ID

SRP OCP LSP ISP DIP

                        88
Vielen Dank!



               89
Credits (1/2)
PPP-Article (online)
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

Photos
http://www.flickr.com/photos/dieterkarner/370967891/
http://www.flickr.com/photos/popcorncx/2221630487/sizes/l/
http://www.flickr.com/photos/bdesham/2432400623/
http://www.flickr.com/photos/popcorncx/2221630487/
http://www.flickr.com/photos/glennbatuyong/4081599002/in/photostream/
http://www.flickr.com/photos/glennbatuyong/4081599168/in/photostream/
http://www.flickr.com/photos/renfield/3865907619/


                                                                        90
                                                                             90
Credits (2/2)
Photos
http://www.flickr.com/photos/renfield/3865907619/
http://www.flickr.com/photos/maxpower/5160699/
http://programmer.97things.oreilly.com/wiki/index.php/Uncle_Bob
http://www.flickr.com/photos/georgivar/3288942086/
http://www.everystockphoto.com/photo.php?imageId=237523
http://www.flickr.com/photos/pasukaru76/3992935923/




                                                                  91
                                                                       91
Lizense


http://creativecommons.org/licenses/by-sa/
  3.0/de/




                                         92
                                              92
Jens-Christian Fischer        Michael Mahlberg

InVisible GmbH                Consulting Guild AG



@jcfischer                     @MMahlberg

jens-christian@invisible.ch   mm@michaelmahlberg.de

http://blog.invisible.ch      http://agile-aspects.blogspot.com




                                                             93
                                                                  93

More Related Content

What's hot

Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
Leonardo Proietti
 
Python advance
Python advancePython advance
Python advance
Mukul Kirti Verma
 
Designing a JavaFX Mobile application
Designing a JavaFX Mobile applicationDesigning a JavaFX Mobile application
Designing a JavaFX Mobile application
Fabrizio Giudici
 
Object Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPObject Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHP
Chad Gray
 
Pim Elshoff "Technically DDD"
Pim Elshoff "Technically DDD"Pim Elshoff "Technically DDD"
Pim Elshoff "Technically DDD"
Fwdays
 
The IoC Hydra
The IoC HydraThe IoC Hydra
The IoC Hydra
Kacper Gunia
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object Calisthenics
Guilherme Blanco
 
Alexander Makarov "Let’s talk about code"
Alexander Makarov "Let’s talk about code"Alexander Makarov "Let’s talk about code"
Alexander Makarov "Let’s talk about code"
Fwdays
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
Leonardo Proietti
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 Application
Kirill Chebunin
 
Web Security - Hands-on
Web Security - Hands-onWeb Security - Hands-on
Web Security - Hands-on
Andrea Valenza
 
Advanced Php - Macq Electronique 2010
Advanced Php - Macq Electronique 2010Advanced Php - Macq Electronique 2010
Advanced Php - Macq Electronique 2010
Michelangelo van Dam
 
Using Dojo
Using DojoUsing Dojo
Using Dojo
Richard Paul
 
Couchbase Korea User Group 2nd Meetup #2
Couchbase Korea User Group 2nd Meetup #2Couchbase Korea User Group 2nd Meetup #2
Couchbase Korea User Group 2nd Meetup #2
won min jang
 
OSDC.fr 2012 :: Cascalog : progammation logique pour Hadoop
OSDC.fr 2012 :: Cascalog : progammation logique pour HadoopOSDC.fr 2012 :: Cascalog : progammation logique pour Hadoop
OSDC.fr 2012 :: Cascalog : progammation logique pour Hadoop
Publicis Sapient Engineering
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016
Kacper Gunia
 
Object Oriented Programming with PHP 5 - More OOP
Object Oriented Programming with PHP 5 - More OOPObject Oriented Programming with PHP 5 - More OOP
Object Oriented Programming with PHP 5 - More OOP
Wildan Maulana
 
Xdebug confoo11
Xdebug confoo11Xdebug confoo11
Xdebug confoo11
Bachkoutou Toutou
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
Konstantin Kudryashov
 
Active Record Inheritance in Rails
Active Record Inheritance in RailsActive Record Inheritance in Rails
Active Record Inheritance in Rails
Sandip Ransing
 

What's hot (20)

Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 
Python advance
Python advancePython advance
Python advance
 
Designing a JavaFX Mobile application
Designing a JavaFX Mobile applicationDesigning a JavaFX Mobile application
Designing a JavaFX Mobile application
 
Object Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHPObject Calisthenics Adapted for PHP
Object Calisthenics Adapted for PHP
 
Pim Elshoff "Technically DDD"
Pim Elshoff "Technically DDD"Pim Elshoff "Technically DDD"
Pim Elshoff "Technically DDD"
 
The IoC Hydra
The IoC HydraThe IoC Hydra
The IoC Hydra
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object Calisthenics
 
Alexander Makarov "Let’s talk about code"
Alexander Makarov "Let’s talk about code"Alexander Makarov "Let’s talk about code"
Alexander Makarov "Let’s talk about code"
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
Rich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 ApplicationRich Model And Layered Architecture in SF2 Application
Rich Model And Layered Architecture in SF2 Application
 
Web Security - Hands-on
Web Security - Hands-onWeb Security - Hands-on
Web Security - Hands-on
 
Advanced Php - Macq Electronique 2010
Advanced Php - Macq Electronique 2010Advanced Php - Macq Electronique 2010
Advanced Php - Macq Electronique 2010
 
Using Dojo
Using DojoUsing Dojo
Using Dojo
 
Couchbase Korea User Group 2nd Meetup #2
Couchbase Korea User Group 2nd Meetup #2Couchbase Korea User Group 2nd Meetup #2
Couchbase Korea User Group 2nd Meetup #2
 
OSDC.fr 2012 :: Cascalog : progammation logique pour Hadoop
OSDC.fr 2012 :: Cascalog : progammation logique pour HadoopOSDC.fr 2012 :: Cascalog : progammation logique pour Hadoop
OSDC.fr 2012 :: Cascalog : progammation logique pour Hadoop
 
The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016The IoC Hydra - Dutch PHP Conference 2016
The IoC Hydra - Dutch PHP Conference 2016
 
Object Oriented Programming with PHP 5 - More OOP
Object Oriented Programming with PHP 5 - More OOPObject Oriented Programming with PHP 5 - More OOP
Object Oriented Programming with PHP 5 - More OOP
 
Xdebug confoo11
Xdebug confoo11Xdebug confoo11
Xdebug confoo11
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Active Record Inheritance in Rails
Active Record Inheritance in RailsActive Record Inheritance in Rails
Active Record Inheritance in Rails
 

Viewers also liked

UnME jeans:Branding in web 2
UnME jeans:Branding in web 2UnME jeans:Branding in web 2
UnME jeans:Branding in web 2
Sameer Mathur
 
Rg 463
Rg 463Rg 463
Rg 463
johnmichal1
 
una pequeña presentación
una pequeña presentación una pequeña presentación
una pequeña presentación
Rumus
 
MASAIGO
MASAIGOMASAIGO
MASAIGO
CARLOS FELIX
 
Ferrovial Investors Presentation Jan Sep 2014 | Presentación Inversores Ene S...
Ferrovial Investors Presentation Jan Sep 2014 | Presentación Inversores Ene S...Ferrovial Investors Presentation Jan Sep 2014 | Presentación Inversores Ene S...
Ferrovial Investors Presentation Jan Sep 2014 | Presentación Inversores Ene S...
Ferrovial
 
Helena Ruiz y Gustavo Lerma Evaluación de Recursos Web
Helena Ruiz y Gustavo Lerma Evaluación de Recursos WebHelena Ruiz y Gustavo Lerma Evaluación de Recursos Web
Helena Ruiz y Gustavo Lerma Evaluación de Recursos Web
Dielmer Fernando Giraldo Rendon
 
Comparación entre skydrive, google drive, zoho docs y thinkfree
Comparación entre skydrive, google drive, zoho docs y thinkfreeComparación entre skydrive, google drive, zoho docs y thinkfree
Comparación entre skydrive, google drive, zoho docs y thinkfree
Senovia_Sarango
 
Presentación Isabel Muñoz-Presidenta del Consejo de Defensa de la Competencia...
Presentación Isabel Muñoz-Presidenta del Consejo de Defensa de la Competencia...Presentación Isabel Muñoz-Presidenta del Consejo de Defensa de la Competencia...
Presentación Isabel Muñoz-Presidenta del Consejo de Defensa de la Competencia...
CompetenciaAnd
 
medikamente-per-klick.de
medikamente-per-klick.demedikamente-per-klick.de
medikamente-per-klick.de
Matthias M. Meringer
 
La viejecita dichosa
La viejecita dichosaLa viejecita dichosa
La viejecita dichosa
HEBER ISRAIN PEREZ LOPEZ
 
NETMIND - Catálogo de Formación 2014 -2015
NETMIND - Catálogo de Formación 2014 -2015NETMIND - Catálogo de Formación 2014 -2015
NETMIND - Catálogo de Formación 2014 -2015
netmind
 
Fundamentos de mercadeo
Fundamentos de mercadeoFundamentos de mercadeo
Fundamentos de mercadeo
Catalina Melo Chaves
 
Acord de govern centralitzacio tic
Acord de govern centralitzacio ticAcord de govern centralitzacio tic
Acord de govern centralitzacio tic
Antoni Davia
 
Story Studio l Storee1
Story Studio l Storee1Story Studio l Storee1
Story Studio l Storee1
제다이
 
Makefile Martial Arts - Chapter 1. The morning of creation
Makefile Martial Arts - Chapter 1. The morning of creationMakefile Martial Arts - Chapter 1. The morning of creation
Makefile Martial Arts - Chapter 1. The morning of creation
Quyen Le Van
 
Campaña Vanilla Dee Lite | LUSH
Campaña Vanilla Dee Lite | LUSHCampaña Vanilla Dee Lite | LUSH
Campaña Vanilla Dee Lite | LUSH
Benito Guerrero
 
Facebook Comparison Report of Top Casinos on the Las Vegas Strip
Facebook Comparison Report of Top Casinos on the Las Vegas StripFacebook Comparison Report of Top Casinos on the Las Vegas Strip
Facebook Comparison Report of Top Casinos on the Las Vegas Strip
Unmetric
 
ARQUITECTURA DEL COMPUTADOR (Plantilla fase 1)
ARQUITECTURA DEL COMPUTADOR (Plantilla fase 1)ARQUITECTURA DEL COMPUTADOR (Plantilla fase 1)
ARQUITECTURA DEL COMPUTADOR (Plantilla fase 1)
DvdM1
 
Reporte sexualidad y familia
Reporte sexualidad y familiaReporte sexualidad y familia
Reporte sexualidad y familia
delacruzs
 
La imagen en la escuela Vs el texto
La imagen en la escuela Vs el textoLa imagen en la escuela Vs el texto
La imagen en la escuela Vs el texto
Marisa Elena Conde
 

Viewers also liked (20)

UnME jeans:Branding in web 2
UnME jeans:Branding in web 2UnME jeans:Branding in web 2
UnME jeans:Branding in web 2
 
Rg 463
Rg 463Rg 463
Rg 463
 
una pequeña presentación
una pequeña presentación una pequeña presentación
una pequeña presentación
 
MASAIGO
MASAIGOMASAIGO
MASAIGO
 
Ferrovial Investors Presentation Jan Sep 2014 | Presentación Inversores Ene S...
Ferrovial Investors Presentation Jan Sep 2014 | Presentación Inversores Ene S...Ferrovial Investors Presentation Jan Sep 2014 | Presentación Inversores Ene S...
Ferrovial Investors Presentation Jan Sep 2014 | Presentación Inversores Ene S...
 
Helena Ruiz y Gustavo Lerma Evaluación de Recursos Web
Helena Ruiz y Gustavo Lerma Evaluación de Recursos WebHelena Ruiz y Gustavo Lerma Evaluación de Recursos Web
Helena Ruiz y Gustavo Lerma Evaluación de Recursos Web
 
Comparación entre skydrive, google drive, zoho docs y thinkfree
Comparación entre skydrive, google drive, zoho docs y thinkfreeComparación entre skydrive, google drive, zoho docs y thinkfree
Comparación entre skydrive, google drive, zoho docs y thinkfree
 
Presentación Isabel Muñoz-Presidenta del Consejo de Defensa de la Competencia...
Presentación Isabel Muñoz-Presidenta del Consejo de Defensa de la Competencia...Presentación Isabel Muñoz-Presidenta del Consejo de Defensa de la Competencia...
Presentación Isabel Muñoz-Presidenta del Consejo de Defensa de la Competencia...
 
medikamente-per-klick.de
medikamente-per-klick.demedikamente-per-klick.de
medikamente-per-klick.de
 
La viejecita dichosa
La viejecita dichosaLa viejecita dichosa
La viejecita dichosa
 
NETMIND - Catálogo de Formación 2014 -2015
NETMIND - Catálogo de Formación 2014 -2015NETMIND - Catálogo de Formación 2014 -2015
NETMIND - Catálogo de Formación 2014 -2015
 
Fundamentos de mercadeo
Fundamentos de mercadeoFundamentos de mercadeo
Fundamentos de mercadeo
 
Acord de govern centralitzacio tic
Acord de govern centralitzacio ticAcord de govern centralitzacio tic
Acord de govern centralitzacio tic
 
Story Studio l Storee1
Story Studio l Storee1Story Studio l Storee1
Story Studio l Storee1
 
Makefile Martial Arts - Chapter 1. The morning of creation
Makefile Martial Arts - Chapter 1. The morning of creationMakefile Martial Arts - Chapter 1. The morning of creation
Makefile Martial Arts - Chapter 1. The morning of creation
 
Campaña Vanilla Dee Lite | LUSH
Campaña Vanilla Dee Lite | LUSHCampaña Vanilla Dee Lite | LUSH
Campaña Vanilla Dee Lite | LUSH
 
Facebook Comparison Report of Top Casinos on the Las Vegas Strip
Facebook Comparison Report of Top Casinos on the Las Vegas StripFacebook Comparison Report of Top Casinos on the Las Vegas Strip
Facebook Comparison Report of Top Casinos on the Las Vegas Strip
 
ARQUITECTURA DEL COMPUTADOR (Plantilla fase 1)
ARQUITECTURA DEL COMPUTADOR (Plantilla fase 1)ARQUITECTURA DEL COMPUTADOR (Plantilla fase 1)
ARQUITECTURA DEL COMPUTADOR (Plantilla fase 1)
 
Reporte sexualidad y familia
Reporte sexualidad y familiaReporte sexualidad y familia
Reporte sexualidad y familia
 
La imagen en la escuela Vs el texto
La imagen en la escuela Vs el textoLa imagen en la escuela Vs el texto
La imagen en la escuela Vs el texto
 

Similar to SOLID Ruby SOLID Rails

SOLID Ruby, SOLID Rails
SOLID Ruby, SOLID RailsSOLID Ruby, SOLID Rails
SOLID Ruby, SOLID Rails
Jens-Christian Fischer
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
rstankov
 
Hierarchical free monads and software design in fp
Hierarchical free monads and software design in fpHierarchical free monads and software design in fp
Hierarchical free monads and software design in fp
Alexander Granin
 
Introduction to Active Record at MySQL Conference 2007
Introduction to Active Record at MySQL Conference 2007Introduction to Active Record at MySQL Conference 2007
Introduction to Active Record at MySQL Conference 2007
Rabble .
 
Responsible JavaScript
Responsible JavaScriptResponsible JavaScript
Responsible JavaScript
Michael Girouard
 
Beyond php - it's not (just) about the code
Beyond php - it's not (just) about the codeBeyond php - it's not (just) about the code
Beyond php - it's not (just) about the code
Wim Godden
 
Beyond php it's not (just) about the code
Beyond php   it's not (just) about the codeBeyond php   it's not (just) about the code
Beyond php it's not (just) about the code
Wim Godden
 
Introduction to Active Record - Silicon Valley Ruby Conference 2007
Introduction to Active Record - Silicon Valley Ruby Conference 2007Introduction to Active Record - Silicon Valley Ruby Conference 2007
Introduction to Active Record - Silicon Valley Ruby Conference 2007
Rabble .
 
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Victor Rentea
 
Why ruby
Why rubyWhy ruby
Why ruby
rstankov
 
2011-02-03 LA RubyConf Rails3 TDD Workshop
2011-02-03 LA RubyConf Rails3 TDD Workshop2011-02-03 LA RubyConf Rails3 TDD Workshop
2011-02-03 LA RubyConf Rails3 TDD Workshop
Wolfram Arnold
 
Beyond PHP - It's not (just) about the code
Beyond PHP - It's not (just) about the codeBeyond PHP - It's not (just) about the code
Beyond PHP - It's not (just) about the code
Wim Godden
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applications
chartjes
 
Tame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapper
Giordano Scalzo
 
Intro to Ruby - Twin Cities Code Camp 7
Intro to Ruby - Twin Cities Code Camp 7Intro to Ruby - Twin Cities Code Camp 7
Intro to Ruby - Twin Cities Code Camp 7
Brian Hogan
 
bmarshall teaching Calculation Manager on prem
bmarshall teaching Calculation Manager on prembmarshall teaching Calculation Manager on prem
bmarshall teaching Calculation Manager on prem
Roma766619
 
Rails Tips and Best Practices
Rails Tips and Best PracticesRails Tips and Best Practices
Rails Tips and Best Practices
David Keener
 
Ruby For Startups
Ruby For StartupsRuby For Startups
Ruby For Startups
Mike Subelsky
 
Extreme Swift
Extreme SwiftExtreme Swift
Extreme Swift
Movel
 
r2con 2017 r2cLEMENCy
r2con 2017 r2cLEMENCyr2con 2017 r2cLEMENCy
r2con 2017 r2cLEMENCy
Ray Song
 

Similar to SOLID Ruby SOLID Rails (20)

SOLID Ruby, SOLID Rails
SOLID Ruby, SOLID RailsSOLID Ruby, SOLID Rails
SOLID Ruby, SOLID Rails
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
Hierarchical free monads and software design in fp
Hierarchical free monads and software design in fpHierarchical free monads and software design in fp
Hierarchical free monads and software design in fp
 
Introduction to Active Record at MySQL Conference 2007
Introduction to Active Record at MySQL Conference 2007Introduction to Active Record at MySQL Conference 2007
Introduction to Active Record at MySQL Conference 2007
 
Responsible JavaScript
Responsible JavaScriptResponsible JavaScript
Responsible JavaScript
 
Beyond php - it's not (just) about the code
Beyond php - it's not (just) about the codeBeyond php - it's not (just) about the code
Beyond php - it's not (just) about the code
 
Beyond php it's not (just) about the code
Beyond php   it's not (just) about the codeBeyond php   it's not (just) about the code
Beyond php it's not (just) about the code
 
Introduction to Active Record - Silicon Valley Ruby Conference 2007
Introduction to Active Record - Silicon Valley Ruby Conference 2007Introduction to Active Record - Silicon Valley Ruby Conference 2007
Introduction to Active Record - Silicon Valley Ruby Conference 2007
 
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
Evolving a Clean, Pragmatic Architecture at JBCNConf 2019
 
Why ruby
Why rubyWhy ruby
Why ruby
 
2011-02-03 LA RubyConf Rails3 TDD Workshop
2011-02-03 LA RubyConf Rails3 TDD Workshop2011-02-03 LA RubyConf Rails3 TDD Workshop
2011-02-03 LA RubyConf Rails3 TDD Workshop
 
Beyond PHP - It's not (just) about the code
Beyond PHP - It's not (just) about the codeBeyond PHP - It's not (just) about the code
Beyond PHP - It's not (just) about the code
 
Building Testable PHP Applications
Building Testable PHP ApplicationsBuilding Testable PHP Applications
Building Testable PHP Applications
 
Tame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapperTame Accidental Complexity with Ruby and MongoMapper
Tame Accidental Complexity with Ruby and MongoMapper
 
Intro to Ruby - Twin Cities Code Camp 7
Intro to Ruby - Twin Cities Code Camp 7Intro to Ruby - Twin Cities Code Camp 7
Intro to Ruby - Twin Cities Code Camp 7
 
bmarshall teaching Calculation Manager on prem
bmarshall teaching Calculation Manager on prembmarshall teaching Calculation Manager on prem
bmarshall teaching Calculation Manager on prem
 
Rails Tips and Best Practices
Rails Tips and Best PracticesRails Tips and Best Practices
Rails Tips and Best Practices
 
Ruby For Startups
Ruby For StartupsRuby For Startups
Ruby For Startups
 
Extreme Swift
Extreme SwiftExtreme Swift
Extreme Swift
 
r2con 2017 r2cLEMENCy
r2con 2017 r2cLEMENCyr2con 2017 r2cLEMENCy
r2con 2017 r2cLEMENCy
 

More from Michael Mahlberg

Heavyweight agile Processes? Let's make them leaner!
Heavyweight agile Processes? Let's make them leaner!Heavyweight agile Processes? Let's make them leaner!
Heavyweight agile Processes? Let's make them leaner!
Michael Mahlberg
 
Skaliert Arbeiten statt zu skalieren - 2022.pdf
Skaliert Arbeiten statt zu skalieren - 2022.pdfSkaliert Arbeiten statt zu skalieren - 2022.pdf
Skaliert Arbeiten statt zu skalieren - 2022.pdf
Michael Mahlberg
 
Agile Tuesday München: Was ist eigentlich aus Lean geworden?
Agile Tuesday München: Was ist eigentlich aus  Lean geworden?Agile Tuesday München: Was ist eigentlich aus  Lean geworden?
Agile Tuesday München: Was ist eigentlich aus Lean geworden?
Michael Mahlberg
 
Process-Tinder – Wenn ich mich nur nach den schönen Bildern entscheide…
Process-Tinder – Wenn ich mich nur nach den schönen Bildern entscheide…Process-Tinder – Wenn ich mich nur nach den schönen Bildern entscheide…
Process-Tinder – Wenn ich mich nur nach den schönen Bildern entscheide…
Michael Mahlberg
 
Was ist aus dem L-Wort (in Lean Kanban) geworden?
Was ist aus dem L-Wort (in Lean Kanban) geworden?Was ist aus dem L-Wort (in Lean Kanban) geworden?
Was ist aus dem L-Wort (in Lean Kanban) geworden?
Michael Mahlberg
 
Immer Ärger mit Jira - LWIPCGN#118 (2021)
Immer Ärger mit Jira - LWIPCGN#118 (2021)Immer Ärger mit Jira - LWIPCGN#118 (2021)
Immer Ärger mit Jira - LWIPCGN#118 (2021)
Michael Mahlberg
 
Continuous Integration - I Don't Think That Word Means What You Think It Means
Continuous Integration - I Don't Think That Word Means What You Think It MeansContinuous Integration - I Don't Think That Word Means What You Think It Means
Continuous Integration - I Don't Think That Word Means What You Think It Means
Michael Mahlberg
 
Flow – DAS Ziel von Kanban?
Flow – DAS Ziel von Kanban?Flow – DAS Ziel von Kanban?
Flow – DAS Ziel von Kanban?
Michael Mahlberg
 
What's in a Story? Drei Ansätze, um mit Anforderungen gemeinsam erfolgreich z...
What's in a Story? Drei Ansätze, um mit Anforderungen gemeinsam erfolgreich z...What's in a Story? Drei Ansätze, um mit Anforderungen gemeinsam erfolgreich z...
What's in a Story? Drei Ansätze, um mit Anforderungen gemeinsam erfolgreich z...
Michael Mahlberg
 
Lwipcgn#110 2020-die agilekeuleueberleben
Lwipcgn#110 2020-die agilekeuleueberlebenLwipcgn#110 2020-die agilekeuleueberleben
Lwipcgn#110 2020-die agilekeuleueberleben
Michael Mahlberg
 
The Trouble with Jira – TAG 2019
The Trouble with Jira – TAG 2019The Trouble with Jira – TAG 2019
The Trouble with Jira – TAG 2019
Michael Mahlberg
 
Michael Mahlberg - Leichtgewichtige Kanban-Metriken auf der LKCE 2018
Michael Mahlberg - Leichtgewichtige Kanban-Metriken auf der LKCE 2018Michael Mahlberg - Leichtgewichtige Kanban-Metriken auf der LKCE 2018
Michael Mahlberg - Leichtgewichtige Kanban-Metriken auf der LKCE 2018
Michael Mahlberg
 
Stances of Coaching - OOP2018
Stances of Coaching - OOP2018Stances of Coaching - OOP2018
Stances of Coaching - OOP2018
Michael Mahlberg
 
From ceremonies to events
From ceremonies to eventsFrom ceremonies to events
From ceremonies to events
Michael Mahlberg
 
The Product Owner's Survival Kit - ein Überblick [DE]
The Product Owner's Survival Kit - ein Überblick [DE]The Product Owner's Survival Kit - ein Überblick [DE]
The Product Owner's Survival Kit - ein Überblick [DE]
Michael Mahlberg
 
What coaching stances can do for you in Kanban settings...
What coaching stances can do for you in Kanban settings... What coaching stances can do for you in Kanban settings...
What coaching stances can do for you in Kanban settings...
Michael Mahlberg
 
A3 thinking - background, process and examples
A3 thinking - background, process and examplesA3 thinking - background, process and examples
A3 thinking - background, process and examples
Michael Mahlberg
 
Lws cologne leansoftwaredevelopment
Lws cologne leansoftwaredevelopmentLws cologne leansoftwaredevelopment
Lws cologne leansoftwaredevelopment
Michael Mahlberg
 
Team models-t4 at2015
Team models-t4 at2015Team models-t4 at2015
Team models-t4 at2015
Michael Mahlberg
 
Ökonomie und Architektur als effektives Duo
Ökonomie und Architektur als effektives DuoÖkonomie und Architektur als effektives Duo
Ökonomie und Architektur als effektives Duo
Michael Mahlberg
 

More from Michael Mahlberg (20)

Heavyweight agile Processes? Let's make them leaner!
Heavyweight agile Processes? Let's make them leaner!Heavyweight agile Processes? Let's make them leaner!
Heavyweight agile Processes? Let's make them leaner!
 
Skaliert Arbeiten statt zu skalieren - 2022.pdf
Skaliert Arbeiten statt zu skalieren - 2022.pdfSkaliert Arbeiten statt zu skalieren - 2022.pdf
Skaliert Arbeiten statt zu skalieren - 2022.pdf
 
Agile Tuesday München: Was ist eigentlich aus Lean geworden?
Agile Tuesday München: Was ist eigentlich aus  Lean geworden?Agile Tuesday München: Was ist eigentlich aus  Lean geworden?
Agile Tuesday München: Was ist eigentlich aus Lean geworden?
 
Process-Tinder – Wenn ich mich nur nach den schönen Bildern entscheide…
Process-Tinder – Wenn ich mich nur nach den schönen Bildern entscheide…Process-Tinder – Wenn ich mich nur nach den schönen Bildern entscheide…
Process-Tinder – Wenn ich mich nur nach den schönen Bildern entscheide…
 
Was ist aus dem L-Wort (in Lean Kanban) geworden?
Was ist aus dem L-Wort (in Lean Kanban) geworden?Was ist aus dem L-Wort (in Lean Kanban) geworden?
Was ist aus dem L-Wort (in Lean Kanban) geworden?
 
Immer Ärger mit Jira - LWIPCGN#118 (2021)
Immer Ärger mit Jira - LWIPCGN#118 (2021)Immer Ärger mit Jira - LWIPCGN#118 (2021)
Immer Ärger mit Jira - LWIPCGN#118 (2021)
 
Continuous Integration - I Don't Think That Word Means What You Think It Means
Continuous Integration - I Don't Think That Word Means What You Think It MeansContinuous Integration - I Don't Think That Word Means What You Think It Means
Continuous Integration - I Don't Think That Word Means What You Think It Means
 
Flow – DAS Ziel von Kanban?
Flow – DAS Ziel von Kanban?Flow – DAS Ziel von Kanban?
Flow – DAS Ziel von Kanban?
 
What's in a Story? Drei Ansätze, um mit Anforderungen gemeinsam erfolgreich z...
What's in a Story? Drei Ansätze, um mit Anforderungen gemeinsam erfolgreich z...What's in a Story? Drei Ansätze, um mit Anforderungen gemeinsam erfolgreich z...
What's in a Story? Drei Ansätze, um mit Anforderungen gemeinsam erfolgreich z...
 
Lwipcgn#110 2020-die agilekeuleueberleben
Lwipcgn#110 2020-die agilekeuleueberlebenLwipcgn#110 2020-die agilekeuleueberleben
Lwipcgn#110 2020-die agilekeuleueberleben
 
The Trouble with Jira – TAG 2019
The Trouble with Jira – TAG 2019The Trouble with Jira – TAG 2019
The Trouble with Jira – TAG 2019
 
Michael Mahlberg - Leichtgewichtige Kanban-Metriken auf der LKCE 2018
Michael Mahlberg - Leichtgewichtige Kanban-Metriken auf der LKCE 2018Michael Mahlberg - Leichtgewichtige Kanban-Metriken auf der LKCE 2018
Michael Mahlberg - Leichtgewichtige Kanban-Metriken auf der LKCE 2018
 
Stances of Coaching - OOP2018
Stances of Coaching - OOP2018Stances of Coaching - OOP2018
Stances of Coaching - OOP2018
 
From ceremonies to events
From ceremonies to eventsFrom ceremonies to events
From ceremonies to events
 
The Product Owner's Survival Kit - ein Überblick [DE]
The Product Owner's Survival Kit - ein Überblick [DE]The Product Owner's Survival Kit - ein Überblick [DE]
The Product Owner's Survival Kit - ein Überblick [DE]
 
What coaching stances can do for you in Kanban settings...
What coaching stances can do for you in Kanban settings... What coaching stances can do for you in Kanban settings...
What coaching stances can do for you in Kanban settings...
 
A3 thinking - background, process and examples
A3 thinking - background, process and examplesA3 thinking - background, process and examples
A3 thinking - background, process and examples
 
Lws cologne leansoftwaredevelopment
Lws cologne leansoftwaredevelopmentLws cologne leansoftwaredevelopment
Lws cologne leansoftwaredevelopment
 
Team models-t4 at2015
Team models-t4 at2015Team models-t4 at2015
Team models-t4 at2015
 
Ökonomie und Architektur als effektives Duo
Ökonomie und Architektur als effektives DuoÖkonomie und Architektur als effektives Duo
Ökonomie und Architektur als effektives Duo
 

Recently uploaded

How to Create a More Engaging and Human Online Learning Experience
How to Create a More Engaging and Human Online Learning Experience How to Create a More Engaging and Human Online Learning Experience
How to Create a More Engaging and Human Online Learning Experience
Wahiba Chair Training & Consulting
 
Main Java[All of the Base Concepts}.docx
Main Java[All of the Base Concepts}.docxMain Java[All of the Base Concepts}.docx
Main Java[All of the Base Concepts}.docx
adhitya5119
 
Pengantar Penggunaan Flutter - Dart programming language1.pptx
Pengantar Penggunaan Flutter - Dart programming language1.pptxPengantar Penggunaan Flutter - Dart programming language1.pptx
Pengantar Penggunaan Flutter - Dart programming language1.pptx
Fajar Baskoro
 
Film vocab for eal 3 students: Australia the movie
Film vocab for eal 3 students: Australia the movieFilm vocab for eal 3 students: Australia the movie
Film vocab for eal 3 students: Australia the movie
Nicholas Montgomery
 
ISO/IEC 27001, ISO/IEC 42001, and GDPR: Best Practices for Implementation and...
ISO/IEC 27001, ISO/IEC 42001, and GDPR: Best Practices for Implementation and...ISO/IEC 27001, ISO/IEC 42001, and GDPR: Best Practices for Implementation and...
ISO/IEC 27001, ISO/IEC 42001, and GDPR: Best Practices for Implementation and...
PECB
 
Reimagining Your Library Space: How to Increase the Vibes in Your Library No ...
Reimagining Your Library Space: How to Increase the Vibes in Your Library No ...Reimagining Your Library Space: How to Increase the Vibes in Your Library No ...
Reimagining Your Library Space: How to Increase the Vibes in Your Library No ...
Diana Rendina
 
Hindi varnamala | hindi alphabet PPT.pdf
Hindi varnamala | hindi alphabet PPT.pdfHindi varnamala | hindi alphabet PPT.pdf
Hindi varnamala | hindi alphabet PPT.pdf
Dr. Mulla Adam Ali
 
বাংলাদেশ অর্থনৈতিক সমীক্ষা (Economic Review) ২০২৪ UJS App.pdf
বাংলাদেশ অর্থনৈতিক সমীক্ষা (Economic Review) ২০২৪ UJS App.pdfবাংলাদেশ অর্থনৈতিক সমীক্ষা (Economic Review) ২০২৪ UJS App.pdf
বাংলাদেশ অর্থনৈতিক সমীক্ষা (Economic Review) ২০২৪ UJS App.pdf
eBook.com.bd (প্রয়োজনীয় বাংলা বই)
 
Exploiting Artificial Intelligence for Empowering Researchers and Faculty, In...
Exploiting Artificial Intelligence for Empowering Researchers and Faculty, In...Exploiting Artificial Intelligence for Empowering Researchers and Faculty, In...
Exploiting Artificial Intelligence for Empowering Researchers and Faculty, In...
Dr. Vinod Kumar Kanvaria
 
ANATOMY AND BIOMECHANICS OF HIP JOINT.pdf
ANATOMY AND BIOMECHANICS OF HIP JOINT.pdfANATOMY AND BIOMECHANICS OF HIP JOINT.pdf
ANATOMY AND BIOMECHANICS OF HIP JOINT.pdf
Priyankaranawat4
 
Advanced Java[Extra Concepts, Not Difficult].docx
Advanced Java[Extra Concepts, Not Difficult].docxAdvanced Java[Extra Concepts, Not Difficult].docx
Advanced Java[Extra Concepts, Not Difficult].docx
adhitya5119
 
C1 Rubenstein AP HuG xxxxxxxxxxxxxx.pptx
C1 Rubenstein AP HuG xxxxxxxxxxxxxx.pptxC1 Rubenstein AP HuG xxxxxxxxxxxxxx.pptx
C1 Rubenstein AP HuG xxxxxxxxxxxxxx.pptx
mulvey2
 
What is Digital Literacy? A guest blog from Andy McLaughlin, University of Ab...
What is Digital Literacy? A guest blog from Andy McLaughlin, University of Ab...What is Digital Literacy? A guest blog from Andy McLaughlin, University of Ab...
What is Digital Literacy? A guest blog from Andy McLaughlin, University of Ab...
GeorgeMilliken2
 
Chapter wise All Notes of First year Basic Civil Engineering.pptx
Chapter wise All Notes of First year Basic Civil Engineering.pptxChapter wise All Notes of First year Basic Civil Engineering.pptx
Chapter wise All Notes of First year Basic Civil Engineering.pptx
Denish Jangid
 
writing about opinions about Australia the movie
writing about opinions about Australia the moviewriting about opinions about Australia the movie
writing about opinions about Australia the movie
Nicholas Montgomery
 
How to Manage Your Lost Opportunities in Odoo 17 CRM
How to Manage Your Lost Opportunities in Odoo 17 CRMHow to Manage Your Lost Opportunities in Odoo 17 CRM
How to Manage Your Lost Opportunities in Odoo 17 CRM
Celine George
 
BÀI TẬP BỔ TRỢ TIẾNG ANH LỚP 9 CẢ NĂM - GLOBAL SUCCESS - NĂM HỌC 2024-2025 - ...
BÀI TẬP BỔ TRỢ TIẾNG ANH LỚP 9 CẢ NĂM - GLOBAL SUCCESS - NĂM HỌC 2024-2025 - ...BÀI TẬP BỔ TRỢ TIẾNG ANH LỚP 9 CẢ NĂM - GLOBAL SUCCESS - NĂM HỌC 2024-2025 - ...
BÀI TẬP BỔ TRỢ TIẾNG ANH LỚP 9 CẢ NĂM - GLOBAL SUCCESS - NĂM HỌC 2024-2025 - ...
Nguyen Thanh Tu Collection
 
How to Setup Warehouse & Location in Odoo 17 Inventory
How to Setup Warehouse & Location in Odoo 17 InventoryHow to Setup Warehouse & Location in Odoo 17 Inventory
How to Setup Warehouse & Location in Odoo 17 Inventory
Celine George
 
How to deliver Powerpoint Presentations.pptx
How to deliver Powerpoint  Presentations.pptxHow to deliver Powerpoint  Presentations.pptx
How to deliver Powerpoint Presentations.pptx
HajraNaeem15
 
South African Journal of Science: Writing with integrity workshop (2024)
South African Journal of Science: Writing with integrity workshop (2024)South African Journal of Science: Writing with integrity workshop (2024)
South African Journal of Science: Writing with integrity workshop (2024)
Academy of Science of South Africa
 

Recently uploaded (20)

How to Create a More Engaging and Human Online Learning Experience
How to Create a More Engaging and Human Online Learning Experience How to Create a More Engaging and Human Online Learning Experience
How to Create a More Engaging and Human Online Learning Experience
 
Main Java[All of the Base Concepts}.docx
Main Java[All of the Base Concepts}.docxMain Java[All of the Base Concepts}.docx
Main Java[All of the Base Concepts}.docx
 
Pengantar Penggunaan Flutter - Dart programming language1.pptx
Pengantar Penggunaan Flutter - Dart programming language1.pptxPengantar Penggunaan Flutter - Dart programming language1.pptx
Pengantar Penggunaan Flutter - Dart programming language1.pptx
 
Film vocab for eal 3 students: Australia the movie
Film vocab for eal 3 students: Australia the movieFilm vocab for eal 3 students: Australia the movie
Film vocab for eal 3 students: Australia the movie
 
ISO/IEC 27001, ISO/IEC 42001, and GDPR: Best Practices for Implementation and...
ISO/IEC 27001, ISO/IEC 42001, and GDPR: Best Practices for Implementation and...ISO/IEC 27001, ISO/IEC 42001, and GDPR: Best Practices for Implementation and...
ISO/IEC 27001, ISO/IEC 42001, and GDPR: Best Practices for Implementation and...
 
Reimagining Your Library Space: How to Increase the Vibes in Your Library No ...
Reimagining Your Library Space: How to Increase the Vibes in Your Library No ...Reimagining Your Library Space: How to Increase the Vibes in Your Library No ...
Reimagining Your Library Space: How to Increase the Vibes in Your Library No ...
 
Hindi varnamala | hindi alphabet PPT.pdf
Hindi varnamala | hindi alphabet PPT.pdfHindi varnamala | hindi alphabet PPT.pdf
Hindi varnamala | hindi alphabet PPT.pdf
 
বাংলাদেশ অর্থনৈতিক সমীক্ষা (Economic Review) ২০২৪ UJS App.pdf
বাংলাদেশ অর্থনৈতিক সমীক্ষা (Economic Review) ২০২৪ UJS App.pdfবাংলাদেশ অর্থনৈতিক সমীক্ষা (Economic Review) ২০২৪ UJS App.pdf
বাংলাদেশ অর্থনৈতিক সমীক্ষা (Economic Review) ২০২৪ UJS App.pdf
 
Exploiting Artificial Intelligence for Empowering Researchers and Faculty, In...
Exploiting Artificial Intelligence for Empowering Researchers and Faculty, In...Exploiting Artificial Intelligence for Empowering Researchers and Faculty, In...
Exploiting Artificial Intelligence for Empowering Researchers and Faculty, In...
 
ANATOMY AND BIOMECHANICS OF HIP JOINT.pdf
ANATOMY AND BIOMECHANICS OF HIP JOINT.pdfANATOMY AND BIOMECHANICS OF HIP JOINT.pdf
ANATOMY AND BIOMECHANICS OF HIP JOINT.pdf
 
Advanced Java[Extra Concepts, Not Difficult].docx
Advanced Java[Extra Concepts, Not Difficult].docxAdvanced Java[Extra Concepts, Not Difficult].docx
Advanced Java[Extra Concepts, Not Difficult].docx
 
C1 Rubenstein AP HuG xxxxxxxxxxxxxx.pptx
C1 Rubenstein AP HuG xxxxxxxxxxxxxx.pptxC1 Rubenstein AP HuG xxxxxxxxxxxxxx.pptx
C1 Rubenstein AP HuG xxxxxxxxxxxxxx.pptx
 
What is Digital Literacy? A guest blog from Andy McLaughlin, University of Ab...
What is Digital Literacy? A guest blog from Andy McLaughlin, University of Ab...What is Digital Literacy? A guest blog from Andy McLaughlin, University of Ab...
What is Digital Literacy? A guest blog from Andy McLaughlin, University of Ab...
 
Chapter wise All Notes of First year Basic Civil Engineering.pptx
Chapter wise All Notes of First year Basic Civil Engineering.pptxChapter wise All Notes of First year Basic Civil Engineering.pptx
Chapter wise All Notes of First year Basic Civil Engineering.pptx
 
writing about opinions about Australia the movie
writing about opinions about Australia the moviewriting about opinions about Australia the movie
writing about opinions about Australia the movie
 
How to Manage Your Lost Opportunities in Odoo 17 CRM
How to Manage Your Lost Opportunities in Odoo 17 CRMHow to Manage Your Lost Opportunities in Odoo 17 CRM
How to Manage Your Lost Opportunities in Odoo 17 CRM
 
BÀI TẬP BỔ TRỢ TIẾNG ANH LỚP 9 CẢ NĂM - GLOBAL SUCCESS - NĂM HỌC 2024-2025 - ...
BÀI TẬP BỔ TRỢ TIẾNG ANH LỚP 9 CẢ NĂM - GLOBAL SUCCESS - NĂM HỌC 2024-2025 - ...BÀI TẬP BỔ TRỢ TIẾNG ANH LỚP 9 CẢ NĂM - GLOBAL SUCCESS - NĂM HỌC 2024-2025 - ...
BÀI TẬP BỔ TRỢ TIẾNG ANH LỚP 9 CẢ NĂM - GLOBAL SUCCESS - NĂM HỌC 2024-2025 - ...
 
How to Setup Warehouse & Location in Odoo 17 Inventory
How to Setup Warehouse & Location in Odoo 17 InventoryHow to Setup Warehouse & Location in Odoo 17 Inventory
How to Setup Warehouse & Location in Odoo 17 Inventory
 
How to deliver Powerpoint Presentations.pptx
How to deliver Powerpoint  Presentations.pptxHow to deliver Powerpoint  Presentations.pptx
How to deliver Powerpoint Presentations.pptx
 
South African Journal of Science: Writing with integrity workshop (2024)
South African Journal of Science: Writing with integrity workshop (2024)South African Journal of Science: Writing with integrity workshop (2024)
South African Journal of Science: Writing with integrity workshop (2024)
 

SOLID Ruby SOLID Rails

  • 1. Michael Mahlberg, Consulting Guild AG Jens-Christian Fischer, InVisible GmbH SOLID Ruby - SOLID Rails Establishing a sustainable codebase 1
  • 2. Who? Michael Mahlberg 2
  • 3. Founder of roughly a dozen companies over the last two decades 3
  • 5. Working as A consultant on software processes, architecture & design for > 2 decades 5
  • 6. >> relevance != nil => true 6
  • 7. Who? Jens-Christian Fischer 7
  • 8. Tinkerer, Practician, Author and generally interested in way too many things 8
  • 10. SOLID is not a Law 10
  • 11. PPP (by Robert C. Martin) Agile Software Development, Principles, Patterns, and Practices 11
  • 12. Principles! You know - more like guidelines 12
  • 13. SOLID SRP OCP LSP ISP DIP 13
  • 14. S OL I D SRP OCP LSP ISP DIP 14
  • 15. SRP Single Responsibility Principle A class should have one, and only one, reason to change. 15
  • 16. require  'digest/sha1' class  User  <  ActiveRecord::Base    include  Authentication User Class    include  Authentication::ByPassword    include  Authentication::ByCookieToken    #TODO  Check  login  redirect  if  this  filter  is  skipped    #skip_after_filter  :store_location    #  Virtual  attribute  for  the  unencrypted  password    attr_accessor  :password    belongs_to  :country    has_one  :user_profile,  :dependent  =>  :destroy    has_one  :note            has_many  :queries    has_many  :tags,  :foreign_key  =>  "created_by"    has_many  :taggings,  :as  =>  :tagger    has_many  :organizations,  :through  =>  :affiliations    has_many  :affiliations    has_many  :locations,  :through  =>  :affiliations    has_many  :projects,  :through  =>  :memberships    has_many  :memberships    has_many  :public_assets,  :through  =>  :privileges    has_many  :descriptions,  :through  =>  :privileges    has_many  :assessments,  :through  =>  :privileges    has_many  :description_profiles,  :through  =>  :privileges    has_many  :privileges    has_many  :diaries    has_many  :roles,  :through  =>  :commitments    has_many  :commitments    has_many  :activities    has_many  :messages    has_many  :fellowships    has_many  :user_groups,  :through  =>  :fellowships    has_many  :survey_responses       16
  • 17. So what‘s wrong with this? 17
  • 18. From: user.rb class User < ActiveRecord::Base include Authentication include Authentication::ByPassword include Authentication::ByCookieToken ... belongs_to :country ... has_one :user_profile, :dependent => :destroy has_many :queries has_many :tags, :foreign_key => "created_by" ... validates_presence_of :login, :email, :country_id validates_presence_of :password, :if => :password_required? ... 18
  • 19. From: user.rb acts_as_state_machine :initial => :pending state :pending, :enter => :make_activation_code state :active, :enter => :do_activate ... event :register do transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| !(u.crypted_password.blank? && u.password.blank?) } end ... def message_threads self.message_threads + self.message_threads end 19
  • 20. From: user.rb def forum_nickname self.user_profile.nickname.blank? ? "#{self.first_name} # {self.last_name}" : self.user_profile.nickname end def name "#{self.first_name} #{self.last_name}" rescue 'n/a' end def email_with_name "#{self.first_name} #{self.last_name} <#{self.email}>" end 20
  • 21. From: user.rb def is_admin? self.roles.collect{|role| role.title}.include?('admin') end def countries [self.country] end 21
  • 22. From: user.rb def boards Board.all :conditions => { :user_group_id => self.user_groups.collect{ |g| g.id }} end def discussions Discussion.all :conditions => { :board_id => self.boards.collect{ |b| b.id }} end def organization_roles role_ids = Affiliation.all(:conditions => {:user_id => self.id}).collect{|a| a.role_id}.uniq roles = Role.find(role_ids) end 22
  • 23. From: user.rb def make_password_reset_code self.password_reset_code = Digest::SHA1.hexdigest ( Time.now.to_s.split(//).sort_by {rand}.join ) end def self.published_users User.all(:conditions => ['state = ?', 'published'], :order => 'login ASC', :include => [:user_profile]) end 23
  • 24. Anyone notice a pattern? 24
  • 27. Authentication Roles Mailers State ... 27
  • 28. So how? Mixins 28
  • 29. New User Model class User < ActiveRecord::Base include Authentication include Authentication::ByPassword include Authentication::ByCookieToken include Project::UserStates include Project::UserMailer include Project::UserForum include Project::UserMessages ... end 29
  • 30. UserMessages module Project module UserMessages # to be included in User Model has_many :messages def message_threads MessageThread.all(:conditions => ["sender_id = ? or receiver_id = ?", self.id, self.id]) end end end 30
  • 31. Methods 31
  • 32. def transfer(data, url) h = Net::HTTP.new(self.uri.host, self.uri.port) RAILS_DEFAULT_LOGGER.debug "connecting to CL: #{self.uri}" RAILS_DEFAULT_LOGGER.debug "connecting to CL: #{url}" resp = h.post(url, data, {'Content-Type' => 'application/xml'}) response_code = resp.code.to_i location = if response_code == 201 resp['Location'] else RAILS_DEFAULT_LOGGER.debug "error from CL: #{response_code}" RAILS_DEFAULT_LOGGER.debug "error from CL: #{resp.body}" @error = resp.body nil end [response_code, location] end 32
  • 33. def transfer(data, document) if document.cl_document_url != nil self.uri = URI.parse(document.cl_document_url ) h = Net::HTTP.new(self.uri.host, self.uri.port) response = h.post(self.uri, data, {'Content-Type' => 'application/xml'}) else h = Net::HTTP.new(self.uri.host, self.uri.port) response = h.post("/tasks", data, {'Content-Type' => 'application/xml'}) end response_code = response.code.to_i if response_code == 201 location = response['Location'] document.cl_document_url = location document.save! else nil end [response_code, location] end 33
  • 34. SRP Transfer def transfer data open_connection post data return location end def open_connection @http = Net::HTTP.new(self.uri.host, self.uri.port) end def post data @response = http.post(self.url, data, {'Content-Type' => 'application/xml'}) end 34
  • 35. def location get_location if created? # returns nil if not created? end def response_code @response.code.to_i end def created? response_code == 201 end def get_location @response['Location'] end def error @response.body end 35
  • 36. Add a 16-band equalizer & a BlueRay player to this... 36
  • 37. And now to this... 37
  • 38. S OL I D SRP OCP LSP ISP DIP 38
  • 39. OCP Open Closed Principle You should be able to extend a classes behavior, without modifying it. 39
  • 40. 40
  • 41. 41
  • 42. 42
  • 43. 43
  • 44. def makemove(map) From the Google x, y = map.my_position # calculate a move ... AI Challenge if(valid_moves.size == 0) map.make_move( :NORTH ) (Tronbot) else # choose move ... puts move # debug (like in the old days) map.make_move( move ) end end class Map ... def make_move(direction) $stdout << ({:NORTH=>1, :SOUTH=>3, :EAST=>2, :WEST=>4}[direction]) $stdout << "n" $stdout.flush end end 44
  • 45. From the Google AI Challenge (Tronbot) def puts(*args) $stderr.puts *args end def p(*args) args.map!{|arg| arg.inspect} puts args end def print(*args) $stderr.print *args end 45
  • 47. class Outputter def initialize(io = $stderr) @io = io end def puts(*args) @io.puts *args end ... end out = Outputter.new out.puts "Testing" 47
  • 48. S OL I D SRP OCP LSP ISP DIP 48
  • 49. LSP Liskov Substitution Principle Derived classes must be substitutable for their base classes. 49
  • 50. No Problem in Ruby Or so it seems... 50
  • 51. No Interface... no problem? 51
  • 52. Wrong ! 52
  • 54. A square is a rectangle 54
  • 55. Rectangle setX setY Square setX setY 55
  • 56. Rectange >> class Rectangle >> attr_accessor :width, :height >> end => nil >> ?> shape = Rectangle.new => #<Rectangle:0x10114fad0> >> shape.width => nil >> shape.width=3 >> shape.width => 3 >> shape.height=5 >> shape.height => 5 >> shape.width => 3 56
  • 57. Square >> class Square ?> def width >> @dimension ?> shape = Square.new >> end => #<Square:0x101107e88> ?> def height ?> puts shape.width >> @dimension nil >> end ?> shape.width=3 ?> def width= n => 3 >> @dimension = n ?> shape.width >> end => 3 ?> def height= n ?> shape.height >> @dimension = n => 3 >> end >> end 57
  • 58. A Problem... >> s = [Rectangle.new, Square.new] => [#<Rectangle:0x1005642e8>, #<Square:0x100564298>] >> a_rectangle = s[rand(2)] => #<Square:0x100564298> >> a_rectangle.height=1 => 1 >> a_rectangle.width=3 => 3 Text >> a_rectangle.height => 3 58
  • 59. CCD Common Conceptual Denominator 59
  • 60. dup 60
  • 61. irb 1:0> 5.respond_to? :dup => true irb 2:0> 5.dup TypeError: can't dup Fixnum from (irb):1:in `dup' from (irb):1 irb 3:0> http://blog.objectmentor.com/articles/2007/02/17/ liskov-substitution-principle-and-the-ruby-core-libraries 61
  • 62. S OL I D SRP OCP LSP ISP DIP 62
  • 63. ISP Interface Segregation Principle Make fine grained interfaces that are client specific. 63
  • 64. 64
  • 65. Users Controller class UsersController < ApplicationController ssl_required :new, :create, :edit, :update, :destroy, :activate, :change_passwort, :forgot_password, :reset_password, :make_profile, :my_contacts ssl_allowed :eula, :index, :show access_control [:suspend, :unsuspend, :destroy, :purge, :delete, :admin, :ban, :remove_ban] => 'admin' before_filter :find_user skip_after_filter :store_location def show unless @user == current_user redirect_to access_denied_path(@locale) else respond_to do |format| format.html format.js { render :partial => "users/#{@context.title}/#{@partial}" } end end end ... 65
  • 66. more UsersController def activate logout_keeping_session! user = User.find_by_activation_code(params[:activation_code]) unless params[:activation_code].blank? case when (!params[:activation_code].blank?) && user && !user.active? user.activate! flash[:notice] = t(:message_sign_up_complete) unless params[:context].blank? redirect_to login_path(:context => params[:context]) else redirect_to "/login" end when params[:activation_code].blank? flash[:error] = t(:message_activation_code_missing) redirect_back_or_default("/") else flash[:error] = t(:message_user_with_that_activation_code_missing) redirect_back_or_default("/") end end 66
  • 67. User Class Revisited class User < ActiveRecord::Base ... end class Registration < ActiveRecord::Base set_table_name "users" acts_as_state_machine :initial => :pending state :pending, :enter => :make_activation_code state :active, :enter => :do_activate ... event :activate do transitions :from => :pending, :to => :active end ... end 67
  • 68. class RegistrationController < ApplicationController ... def activate logout_keeping_session! code_is_blank = params[:activation_code].blank? registration = Registration.find_by_activation_code(params [:activation_code]) unless code_is_blank case when (!code_is_blank) && registration && !registratio.active? registration.activate! flash[:notice] = t(:message_sign_up_complete) unless params[:context].blank? redirect_to login_path(:context => params[:context]) else redirect_to "/login" end when code_is_blank flash[:error] = t(:message_activation_code_missing) redirect_back_or_default("/") else flash[:error] = t(:message_user_with_that_activation_code_missing) redirect_back_or_default("/") end end ... end 68
  • 69. S OL I D SRP OCP LSP ISP DIP 69
  • 70. DIP Dependency Inversion Principle Depend on abstractions, not on concretions. 70
  • 71. 71
  • 72. From our OCP example to DIP out = Outputter.new out.puts "Testing" 72
  • 73. The code we wish we had class TronBot def initialize @@out = TRON_ENVIRONMENT[:debugger] end def some_method ... @@out.puts "Testing" ... end end 73
  • 74. TSTTCPW TRON_ENVIRONMENT = { :debugger => Outputter.new ($stderr), :game_engine => Outputter.new ($stdout), :user_io => Outputter.new ($stderr) } 74
  • 75. Later... TRON_ENVIRONMENT = { :debugger => Outputter.new ($stderr), :game_engine => Outputter.new (TCP_OUTPUTTER), :user_io => Outputter.new ($stderr) } 75
  • 76. DIP Violation in Controller format.js do render :update do |page| if @parent_object.class == EspGoal @esp_goal_descriptor = @current_object page.replace_html "descriptor_#{@current_object.id}", :partial => "edit_esp_goal_descriptor", :locals => {:esp_goal_descriptor => @esp_goal_descriptor, :parent_object => @parent_object} else @goal_descriptor = @current_object page.replace_html "descriptor_#{@current_object.id}", :partial => "edit_goal_descriptor", :locals => {:goal_descriptor => @goal_descriptor, :parent_object => @parent_object} end end end 76
  • 77. DIP Violation in Controller format.js do render :update do |page| if @parent_object.class == EspGoal @esp_goal_descriptor = @current_object page.replace_html "descriptor_#{@current_object.id}", :partial => "edit_esp_goal_descriptor", :locals => {:esp_goal_descriptor => @esp_goal_descriptor, :parent_object => @parent_object} else if @parent_object.class == Goal @goal_descriptor = @current_object page.replace_html "descriptor_#{@current_object.id}", :partial => "edit_goal_descriptor", :locals => {:goal_descriptor => @goal_descriptor, :parent_object => @parent_object} else if @parent_object.class == LearningGoal ... ... end end end 77
  • 78. 78
  • 79. 1st Refactoring def show ... format.js do render :update do |page| page.replace_html "descriptor_#{@current_object.id}", @parent_object.page_replacement(@current_object) end end end class EspGoal def page_replacement child { :partial => "edit_esp_goal_descriptor", :locals => {:esp_goal_descriptor => child, :parent_object => self} } end end class Goal def page_replacement child { :partial => "edit_goal_descriptor", :locals => {:goal_descriptor => child, :parent_object => self} } end end 79
  • 80. 80
  • 81. 2nd Refactoring (wiring) class PartialContainer def add class_symbol, partial_replacement @@partinal_replacements.add( class_symbol => partial_replacement) end def self.partial_replacement an_object unless @@partial_replacments self.add( EspGoalReplacement.my_class_sym, EspGoalReplacment.new) self.add( GoalReplacement.my_class_sym, GoalReplacment.new) end @@partial_replacement[an_object.class] end end 81
  • 82. class EspGoalReplacmenent 2nd Refactoring def self.my_class_sym end EspGoal.to_sym (Behaviour) def partial_definition child { :partial => "edit_esp_goal_descriptor", :locals => {:esp_goal_descriptor => child, :parent_object => child.esp_goal} } end end class GoalReplacmenent def self.my_class_sym Goal.to_sym end def partial_definition child { :partial => "edit_goal_descriptor", :locals => {:goal_descriptor => child, :parent_object => child.goal} } end end 82
  • 83. DIP Violation in Controller format.js do render :update do |page| if @parent_object.class == EspGoal @esp_goal_descriptor = @current_object page.replace_html "descriptor_#{@current_object.id}", :partial => "edit_esp_goal_descriptor", :locals => {:esp_goal_descriptor => @esp_goal_descriptor, :parent_object => @parent_object} else @goal_descriptor = @current_object page.replace_html "descriptor_#{@current_object.id}", :partial => "edit_goal_descriptor", :locals => {:goal_descriptor => @goal_descriptor, :parent_object => @parent_object} end end end 83
  • 84. 2nd Refactoring - the Controller - def show ... format.js do render :update do |page| page.replace_html "descriptor_#{@current_object.id}", PartialContainer.partial_replacement(@parent_object). partial_definition(@current_object) end end end 84
  • 85. 85
  • 86. SOLID SRP OCP LSP ISP DIP 86
  • 87. SRP OCP LSP ISP DIP 87
  • 88. Questions? S OL ID SRP OCP LSP ISP DIP 88
  • 93. Jens-Christian Fischer Michael Mahlberg InVisible GmbH Consulting Guild AG @jcfischer @MMahlberg jens-christian@invisible.ch mm@michaelmahlberg.de http://blog.invisible.ch http://agile-aspects.blogspot.com 93 93

Editor's Notes