Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

0

Share

Download to read offline

Contracts in Ruby - Vladyslav Hesal

Download to read offline

Ruby Meditation #14
8th of April, 2017
Kyiv, Projector

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all
  • Be the first to like this

Contracts in Ruby - Vladyslav Hesal

  1. 1. Vladyslav Hesal Github @arantir Twitter @hesalx
  2. 2. Contracts in Ruby
  3. 3. What are contracts, how to implement them in Ruby, why it’s hard, and do you really need them
  4. 4. TL;DR The central idea of DbC is a metaphor on how elements of a software system collaborate with each other on the basis of mutual obligations and benefits. The metaphor comes from business life, where a "client" and a "supplier" agree on a "contract" that defines for example that: ● The supplier must provide a certain product (obligation) and is entitled to expect that the client has paid its fee (benefit). ● The client must pay the fee (obligation) and is entitled to get the product (benefit). ● Both parties must satisfy certain obligations, such as laws and regulations, applying to all contracts. Similarly, if a routine from a class in object-oriented programming provides a certain functionality, it may: ● Expect a certain condition to be guaranteed on entry by any client module that calls it: the routine's precondition—an obligation for the client, and a benefit for the supplier (the routine itself), as it frees it from having to handle cases outside of the precondition. ● Guarantee a certain property on exit: the routine's postcondition—an obligation for the supplier, and obviously a benefit (the main benefit of calling the routine) for the client. ● Maintain a certain property, assumed on entry and guaranteed on exit: the class invariant. The notion of a contract extends down to the method/procedure level; the contract for each method will normally contain the following pieces of information:[citation needed] ● Acceptable and unacceptable input values or types, and their meanings ● Return values or types, and their meanings ● Error and exception condition values or types that can occur, and their meanings ● Side effects ● Preconditions ● Postconditions ● Invariants ● (more rarely) Performance guarantees, e.g. for time or space used
  5. 5. TL;DR ● Preconditions ● Postconditions ● Invariants
  6. 6. Simply Subclasses are allowed to: ● weaken preconditions (but not strengthen them) ● strengthen postconditions and invariants (but not weaken)
  7. 7. TypeScript?
  8. 8. TypeScript?
  9. 9. TypeScript? function foo(key, value) { if (typeof key !== "string") { throw new Error(`Expected key to be string.`); } if (typeof value !== "number") { throw new Error(`Expected value to be number.`); } /// ...code... } function foo(key: string, value: number) { /// ...code... }
  10. 10. D Contract Programming in { ...contract preconditions... } out (result) { ...contract postconditions... } body { ...code... }
  11. 11. D Contract Programming long square_root(long x) in { assert(x >= 0); } out (result) { assert((result * result) <= x && (result+1) * (result+1) > x); } body { return cast(long)std.math.sqrt(cast(real)x); }
  12. 12. D Contract Programming class Date { int day; int hour; this(int d, int h) { day = d; hour = h; } invariant { assert(1 <= day && day <= 31); assert(0 <= hour && hour < 24); } }
  13. 13. Ruby Contract Programming Contract Num => Num def double(x) x * 2 end github:egonSchiele/contracts.ruby
  14. 14. Ruby Contract Programming class BankAccount attr_reader :balance class << self def less_than_balance? all? positive_number?, clause {|n| n <= balance} end end contract less_than_balance? => positive_number? def withdraw(amount) new_balance = @balance - amount @balance = new_balance return new_balance end end github:bguthrie/handshake
  15. 15. Ruby Contract Programming def division(a, b) DBC.require(b != 0, "Divisor must be non-zero") a / b end github:matholroyd/dbc
  16. 16. Ruby Contract Programming pre { ...contract preconditions... } post { |result| ...contract postconditions... } def method ...code... end
  17. 17. Ruby Contract Programming class Math pre { assert x.is_a?(Numeric) } post { |result| assert result.is_a?(Numeric) } def double(x, y) x * 2 end end
  18. 18. Noticeable things def delegate(bounded_method, *args, **kwargs, &block) # just maybe block return bounded_method.(&block) if args.empty? && kwargs.empty? # just regular args and maybe block return bounded_method.(*args, &block) if !args.empty? && kwargs.empty? # just keyword args and maybe block return bounded_method.(**kwargs, &block) if args.empty? && !kwargs.empty? # regular args and keyword args and maybe block bounded_method.(*args, **kwargs, &block) end Transparent arguments passing: wrong way
  19. 19. Noticeable things def method_missing(m, *args, &block) r = true target = self.__getobj__ {r = false} if r && target.respond_to?(m) target.__send__(m, *args, &block) elsif ::Kernel.respond_to?(m, true) ::Kernel.instance_method(m).bind(self).(*args, &block) else super(m, *args, &block) end end stdlib’s Delegator#method_missing
  20. 20. Noticeable things def delegate(m, *args, &block) target.__send__(m, *args, &block) end Transparent arguments passing: ok way
  21. 21. Noticeable things Method visibility class Foo def foo; end def bar; end private :foo private def bax; end end class Bar < Foo private def foo; end end
  22. 22. Noticeable things Method visibility ● no callbacks ● dynamic visibility changing ● unrestricted subclass overrides
  23. 23. Noticeable things Accessing method arguments elsewhere Kernel#binding def get_binding(param) return binding end b = get_binding("hello") b.eval("param") #=> "hello"
  24. 24. Noticeable things Default methods arguments
  25. 25. Noticeable things Default methods arguments
  26. 26. Noticeable things Accessing method arguments elsewhere inheritance, prepend, include ??? UnboundMethod#parameters
  27. 27. Noticeable things def args_def(method) method.parameters.map do |param| args << param[1] case param[0] when :opt # optional regular argument "#{param[1]} = nil" when :rest # splat regular arguments "*#{param[1]}" when :key # optional keyword argument "#{param[1]}: nil" when :keyreq # required keyword argument "#{param[1]}:" when :keyrest # splat keyword arguments "**#{param[1]}" when :block # block "&#{param[1]}" else param[1] end end.join(', ') end
  28. 28. Ruby Contracts Aspect class_eval(<<-RUBY, __FILE__, __LINE__ + 1) def #{method_name}(#{args_def}) return super unless respond_to?(__method__, false) __contract__.contract_eval(self, __method__, binding) { super } end RUBY
  29. 29. def #{method_name}(#{arg return super unless respo __contract__.contract_e end

Ruby Meditation #14 8th of April, 2017 Kyiv, Projector

Views

Total views

235

On Slideshare

0

From embeds

0

Number of embeds

2

Actions

Downloads

0

Shares

0

Comments

0

Likes

0

×