Se ha denunciado esta presentación.
Se está descargando tu SlideShare. ×

The Trailblazer Ride from the If Jungle into a Civilised Railway Station - Orban Botond (ENG) | Ruby Meditation 27

Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Cargando en…3
×

Eche un vistazo a continuación

1 de 49 Anuncio

The Trailblazer Ride from the If Jungle into a Civilised Railway Station - Orban Botond (ENG) | Ruby Meditation 27

Descargar para leer sin conexión

Speech of Orban Botond, Ruby Developer at Toptal, at Ruby Meditation 27, Dnipro, 19.05.2019
Slideshare -
Next conference - http://www.rubymeditation.com/

Software development is a domain where everybody can make a beautiful sculpture or can quickly build an unsolvable maze. According to my observations the later happens more often unfortunately.
In the title for my presentation both the If Jungle and the Civilised Railway Station are methaphors representing the opposite ends of the scale of quality code.
In my presentation I am going to present my personal experiences on how to get out from the trap of the if jungle by making the code to adhere to the SRP and DRY principles. I am also going to show the advantages of the Railway Oriented Programing using the 3 different libraries.
The examples are going to be stereotypical errors, fun and easy to follow.

Announcements and conference materials https://www.fb.me/RubyMeditation
News https://twitter.com/RubyMeditation
Photos https://www.instagram.com/RubyMeditation
The stream of Ruby conferences (not just ours) https://t.me/RubyMeditation

Speech of Orban Botond, Ruby Developer at Toptal, at Ruby Meditation 27, Dnipro, 19.05.2019
Slideshare -
Next conference - http://www.rubymeditation.com/

Software development is a domain where everybody can make a beautiful sculpture or can quickly build an unsolvable maze. According to my observations the later happens more often unfortunately.
In the title for my presentation both the If Jungle and the Civilised Railway Station are methaphors representing the opposite ends of the scale of quality code.
In my presentation I am going to present my personal experiences on how to get out from the trap of the if jungle by making the code to adhere to the SRP and DRY principles. I am also going to show the advantages of the Railway Oriented Programing using the 3 different libraries.
The examples are going to be stereotypical errors, fun and easy to follow.

Announcements and conference materials https://www.fb.me/RubyMeditation
News https://twitter.com/RubyMeditation
Photos https://www.instagram.com/RubyMeditation
The stream of Ruby conferences (not just ours) https://t.me/RubyMeditation

Anuncio
Anuncio

Más Contenido Relacionado

Presentaciones para usted (20)

Similares a The Trailblazer Ride from the If Jungle into a Civilised Railway Station - Orban Botond (ENG) | Ruby Meditation 27 (20)

Anuncio

Más de Ruby Meditation (20)

Más reciente (20)

Anuncio

The Trailblazer Ride from the If Jungle into a Civilised Railway Station - Orban Botond (ENG) | Ruby Meditation 27

  1. 1. From The If Jungle to A Civilised Railway Station By Botond Orban
  2. 2. About Me Botond Orban Enthusiast IT Guy, Architect Enthusiastic about Ruby
 https://github.com/orbanbotond
 @orbanbotond
  3. 3. The Origins The PC made in Ukraine
  4. 4. Code can be written in a better way!
  5. 5. Railway Oriented Development With 3 libraries in Parallel! -classic if jungle -dry transactions -monad do notation -trailblazer operations
  6. 6. def create user = User.create user_params[:user] if user.valid? package = Package.create package_params[:user] if package.valid? user.package = package user.save SmsService.send_registration_msg(user, package) EmailService.send_registration_msg(user, package) SystemNotifierService.send_registration_msg(user, package) else render ... end else render ... end end True If Jungle Cyclomatic complexity: 2
  7. 7. def create user = User.create user_params[:user] if user.valid? package = Package.create package_params[:user] if package.valid? package.user = user package.save if params[:coupon].present? if coupon = Coupon.exists? params[:coupon] discount = Discount.create params[:coupon] package.discount = discount package.save else render … end end SmsService.send_registration_msg(user, package) EmailService.send_registration_msg(user, package) SystemNotifierService.send_registration_msg(user, package) else render ... end else render ... end end True If Jungle Cyclomatic complexity: 4
  8. 8. True If Jungle def create user = User.create user_params[:user] if user.valid? package = Package.create package_params[:user] if package.valid? package.user = user package.save if params[:coupon].present? if Coupon.exists? params[:coupon] discount = Discount.create params[:coupon] if discount.allowsUser? user package.discount = discount package.save else render ... end else render ... end end SmsService.send_registration_msg(user, package) EmailService.send_registration_msg(user, package) SystemNotifierService.send_registration_msg(user, package) else render ... end else render ... end end Cyclomatic complexity: 5
  9. 9. Example 1 f(x) = ax+b
  10. 10. No Library If Jungle
  11. 11. If Jungle Implementation Specs: f(a,b) = a+b, a is infinite context 'add' do subject { add.call params: params } 
 context 'negative cases' do context 'params infinite' do let(:params) { [(1.0/0.0), 2] } specify 'Be a failure with a proper error message' do expect(subject[:validation]).to eq 'must be a real number' end end end
  12. 12. add = ->(params:) do return { validation: 'must be a real number' } if params.any?{|x|x.infinite?} end Code: If Jungle Implementation f(a,b) = a+b, a is infinite
  13. 13. Specs: context 'add' do subject { add.call params: params } let(:params) { [2,3,4] } context 'negative cases' do … context 'positive cases' do specify 'Be a success with the proper correct output' do expect(subject[:operation_result]).to eq(params.reduce(0) { |acc, x| acc + x }) end end end If Jungle Implementation f(a,b) = a+b,
  14. 14. Code: add = ->(params:) do return { validation: 'must be a real number' } if params.any?{|x|x.infinite?} result = params.reduce(0) { |acc, x| acc + x } return { operation_result: result } end If Jungle Implementation f(a,b) = a+b,
  15. 15. linear_function = ->(params:) do result = multiply.call(params: params[-2..-1]) if(result[:operation_result]) return add.call(params: [result[:operation_result], params[0]]) else return result end end rspec spec/railway_oriented_development/if_jungle_spec.rb ....... 7 examples, 0 failures If Jungle Implementation f(a,b,x) = a.x+b, Linear function
  16. 16. Building Blocks Add Operation -Guard Condition -Business Logic: + Multiply Operation -Guard Condition -Business Logic: * LinearFunction -Delegation -Conditional -Delegation -Return a Result If Jungle Implementation
  17. 17. 1st library Dry Transactions
  18. 18. Specs: context 'add' do subject { DryTransactions::Add.new.call params } 
 context 'negative cases' do context 'params infinite' do let(:params) { {params:[(1.0/0.0), 2]} } specify 'Be a failure with a proper error message' do expect(subject).to be_failure expect(subject.failure).to eq 'must be a real number' end end end Dry Transactions f(a,b) = a+b, a is infinite
  19. 19. Code: module DryTransactions class Add include Dry::Transaction step :validate step :add private … … Dry Transactions f(a,b) = a+b, a is infinite
  20. 20. Railway Oriented Approach
  21. 21. Code: module DryTransactions class Add … private def validate(input) return Failure('must be a real number') unless input.all?{|x| x.finite?} Success(input) end
 def add(input) ret = input.reduce(0) { |acc, x| acc + x } Success(ret) end … Dry Transactions f(a,b) = a+b, a is infinite
  22. 22. class LinearOperation include Dry::Transaction # a*x step :multiply # previous_result + b step :assembling_partial_results private … end Dry Transactions f(a, b, x) = a.x+b, Linear Function
  23. 23. class ComplexOperation include Dry::Transaction … def multiply(input) partialValue = Multiply.new.call(params: [input[:params] [1], input[:params][2]]) partialValue.bind do |value| Success(input.merge(multiplication_result: partialValue.value!)) end end def assembling_partial_results(input) Add.new.call( params: [input[:params][0], input[:multiplication_result]]) end end rspec spec/railway_oriented_development/dry_transactions_spec.rb ....... 7 examples, 0 failures Dry Transactions f(a, b, x) = a.x+b, a is infinite
  24. 24. Dry Transaction Implementation Add Operation -Guard Step -Business Logic Step: + Multiply Operation -Guard Step -Business Step: * LinearFunction -Multiply Step -Assemble Step Note: -no conditional compared to the If Jungle Solution! -linear execution by enlisting the steps!
  25. 25. 2rd library Monad Do Notation
  26. 26. Specs: context 'add' do subject { MonadDoNotation::Add.new.call params } 
 context 'negative cases' do context 'params infinite' do let(:params) { [(1.0/0.0), 2] } specify 'Be a failure with a proper error message' do expect(subject).to be_failure expect(subject.failure[:validation]).to eq 'must be a real number' end end end Monad Do Notation f(a,b) = a+b, a is infinite
  27. 27. Code: class Add include Dry::Monads::Result::Mixin include Dry::Monads::Do::All def call(arguments) validation_result = yield validate(arguments) operation_result = yield add(arguments) Success validation_result.merge( operation_result) end … end Monad Do Notation f(a,b) = a+b, a is infinite
  28. 28. Code: class Add … def validate(input) return Failure(validation: 'must be a real number') unless input.all?{|x|x.finite?} Success(validation: :ok) end def add(input) ret = input.reduce(0) { |acc, x| acc + x } Success(operation_result: ret) end end … Monad Do Notation f(a,b) = a+b, a is infinite
  29. 29. class LinearOperation include Dry::Monads::Result::Mixin include Dry::Monads::Do::All def call(input) multiplication = yield multiply(input[-2..-1]) addition = yield add([input[0], multiplication[:operation_result]]) Success(addition) end private … Monad Do Notation f(a, b, x) = a.x+b,
  30. 30. class LinearOperation include Dry::Monads::Result::Mixin include Dry::Monads::Do::All … private def multiply(args) Multiply.new.call args end def add(args) Add.new.call args end end rspec spec/railway_oriented_development/monad_do_notation_spec.rb ....... 7 examples, 0 failures Monad Do Notation f(a, b, x) = a.x+b,
  31. 31. Add Operation -Validate Method -Business Logic Method: + Multiply Operation -Validate Method -Business Logic Method: * LinearFunction -Multiply Method -Add Method Step Note: -no conditional compared to the If Jungle Solution! -linear execution! -pure Ruby! ***** Monad Do Notation Implementation
  32. 32. 3rd library Trailblazer Operations
  33. 33. Specs: context 'add' do subject { TrailblazerOperations::Add.call params: params } 
 context 'negative cases' do context 'params infinite' do let(:params) { [(1.0/0.0), 2] } specify 'Be a failure with a proper error message' do expect(subject).to be_failure expect(subject[:validation]).to eq 'must be a real number' end end end Trailblazer Operations f(a,b) = a+b, a is infinite
  34. 34. Code: module TrailblazerOperations class Add < Trailblazer::Operation step :validate step :add private … end End Trailblazer Operations f(a,b) = a+b, a is infinite
  35. 35. Railway Oriented Approach
  36. 36. Code: module TrailblazerOperations class Add < Trailblazer::Operation … private def validate(options, params:) unless params.all?{|x|x.finite?} options[:validation] = 'must be a real number' return Railway.fail! end Railway.pass! end def add(options, params:, **rest) ret = params.reduce(0) { |acc, x| acc + x } options[:operation_result] = ret end end end end
  37. 37. Code: class LinearOperation < Trailblazer::Operation step Nested( Multiply, input: -> (options, params:, **) do options.merge params: params[-2..-1] end ) step Nested( Add, input: -> (options, params:, **) do options.to_hash.except(:operation_result).merge params: [params[0], options[:operation_result]] end ) end rspec spec/railway_oriented_development/trailblazer_operations_spec.rb ....... 7 examples, 0 failures Trailblazer Operations f(a, b, x) = a.x+b,
  38. 38. Add Operation -Validate Step -Business Logic Step: + Multiply Operation -Validate Step -Business Logic Step: * LinearFunction -Delegates to the Multiply Operation by Nesting -Delegates to the Add Operation by Nesting Note: -no conditional compared to the If Jungle Solution! -linear execution! -DSL for reuse! Trailblazer Implementation
  39. 39. Dry-Transaction Monad Do Notation Trailblazer Steps Steps Ruby Code Wrapped With Yield Steps Code Reuse Ruby Call Ruby Code Wrapped With Yield DSL for other Operation Reuse!
  40. 40. True If Jungle def create user = User.create user_params[:user] if user.valid? package = Package.create package_params[:user] if package.valid? package.user = user package.save if params[:coupon].present? if Coupon.exists? params[:coupon] discount = Discount.create params[:coupon] if discount.allowsUser? user package.discount = discount package.save else render ... end else render ... end end SmsService.send_registration_msg(user, package) EmailService.send_registration_msg(user, package) SystemNotifierService.send_registration_msg(user, package) else render ... end else render ... end end Cyclomatic complexity: 5
  41. 41. Railway Oriented Development Cyclomatic complexity: 1-2 class Add < Trailblazer::Operation step :persist_user step :persist_package step :add_coupon_based_discount step :notify_about_registration ... end
  42. 42. Railway Oriented Development Cyclomatic complexity: 1-2 class Add < Trailblazer::Operation step :persist_user failure :log_user_persistance step :persist_package failure :log_package_persistance step :add_coupon_based_discount failure :log_discount_creation step :notify_about_registration ... end
  43. 43. Railway Oriented Development Cyclomatic complexity: 1-2 class Add < Trailblazer::Operation step :persist_user failure :log_user_persistance step :persist_package failure :log_package_persistance step :add_coupon_based_discount failure :log_discount_creation step :notify_about_registration step :notify_facebook_friends … end
  44. 44. Contract for every input (entity & other use case)
 
 Basement:
 -Operations for CRUD 
 Crud (and Other) Reuse (DRY): -Operations for Onboarding -Operations for Admin -Operations for handling Business Use Cases Trailblazer Operations & Contracts (Reform) My Best Practice
  45. 45. Thank you ;) Botond Orban Enthusiast IT Guy, Architect Enthusiastic about Ruby
 https://github.com/orbanbotond
 @orbanbotond

×