Upload
piotr-solnica
View
769
Download
3
Embed Size (px)
Citation preview
Patterns, rules, principles, best practices, code smells, metric tools, refactoring, TDD, mutation testing, …
arr = ["foo", "bar"]
arr.freeze
arr << "baz"
# `<main>': can't modify frozen Array (RuntimeError)
arr[0].upcase!
arr
# ["FOO", "bar"]
require 'ice_nine'
arr = ["foo", "bar"]
IceNine.deep_freeze(arr)
arr[0].upcase!
# `upcase!': can't modify frozen String
# (RuntimeError)
post = Post.new(["red", "green", "blue"])
post.tags[0].upcase!
post
#<Post:0x007fc69c1785d0 @tags=["RED", "green",
"blue"]>
require 'adamantium'
class FrozenPost include Adamantium
attr_reader :tags
def initialize(tags) @tags = tags endend
post = FrozenPost.new(["red", "green", "blue"])
post.tags[0].upcase!
# `upcase!': can't modify frozen String (RuntimeError)
class MutablePost attr_reader :tags
def initialize(tags) @tags = tags endend
class FrozenPost include Adamantium
attr_reader :tags
def initialize(tags) @tags = tags endend
Benchmark.ips do |x| x.report('MutablePost') { MutablePost.new(["red", "green", “blue"]) }
x.report('FrozenPost') { FrozenPost.new(["red", "green", “blue"]) }
x.compare!end
# Calculating -------------------------------------# MutablePost 81.643k i/100ms# FrozenPost 12.311k i/100ms# -------------------------------------------------# MutablePost 1.850M (± 5.3%) i/s - 9.226M# FrozenPost 139.753k (± 4.0%) i/s - 701.727k## Comparison:# MutablePost: 1849628.0 i/s# FrozenPost: 139752.8 i/s - 13.23x slower
arr.map do |user| { user_name: user[:name], user_age: user[:age] }end# [{:user_name=>"Joe", :user_age=>21}, # :user_name=>”Jane", :user_age=>20}]
BlockReceives a hash
Returns a new hash
arr.map(&prefix)# [{:user_name=>"Joe", :user_age=>21},# {:user_name=>"Jane", :user_age=>20}]
prefix = -> user do { user_name: user[:name], user_age: user[:age] }end
lambda
pass lambda as block
pluck_age = Mappings.method(:pluck_age)# #<Method: Mappings.pluck_age>
module Mappings def self.pluck_age(arr) arr.map { |user| user[:age] } endend
pluck_age.call(arr)
require 'transproc'
module Mappings extend Transproc::Registry
def self.pluck_age(arr) arr.map { |user| user[:age] } endend
module Mappings extend Transproc::Registry
def self.pluck(hash, key) hash[key] endend
t[:pluck, :age].call(name: 'Jane', age: 20)# 20
module Mappings extend Transproc::Registry
def self.pluck(hash, key) hash[key] end
def self.upcase(input) input.upcase endend
transformation = t[:pluck, :name] >> t[:upcase]
transformation.call(name: 'Jane', age: 20)# "JANE"
Pipeline composition operator
require 'transproc/all'require 'inflecto'
module Mappings extend Transproc::Registry
import :extract_key, from: Transproc::ArrayTransformations
import :underscore, from: Inflectoend
External library
Transproc’s builtin transformations
transformation.call( [{ field: ‘UserName' }, { field: 'UserAddress' }])# ["user_name", "user_address"]
transformation = t[:extract_key, :field] >> t[:underscore]
…SOME RUBY GEMS IMPLEMENTING VARIOUS MONADS
▸ https://github.com/tomstuart/monads
▸ https://github.com/pzol/monadic
▸ https://github.com/pzol/deterministic
▸ https://github.com/txus/kleisli
require 'kleisli'
data = Maybe(csv_file).fmap do |value| begin Right(CSV.parse(value.read)) rescue ParseError => e Left(e.message) endend
Maybe
Either
validated = data.fmap do |value| errors = validate.call(value)
if errors Left(errors) else Right(value) endend
persisted = validated.fmap do |value| result = persist_data.call(value)
if result.success? Right(result.value) else Left(result.error) endend
persisted.fmap do |result| # do something with the resultend
persisted.or do |error| # do something with the errorend
app_container.register(:database) { Database.new}
app_container.register(:validate_user) { ValidateUser.new}
app_container.register(:persist_user) { PersistUser.new}
class PersistUser include Import[:database, :validate_user]
def call(input) input.fmap do |user| errors = validate_user.call(user)
if errors Left(errors) else Right(database[:users].insert(user)) end end endend