Simple and elegant API on top of the complex but powerful one
23
palkan
palkan_tula
SouthEastRuby ‘18
Slide 24
SENSIBLE DEFAULTS
Slide 25
SENSIBLE DEFAULTS class Post < ActiveRecord!::Base # in the world with no defaults self.table_name = “posts” belongs_to :user, foreign_key: :user_id, class_name: “User”, primary_key: :id end
Sensible? 🤔 25
palkan
palkan_tula
SouthEastRuby ‘18
Slide 26
SENSIBLE DEFAULTS class Post < ActiveRecord!::Base belongs_to :user end
N O TI
N E V N R CO E N V O O I T A R U G I F N O C 26
palkan
palkan_tula
SouthEastRuby ‘18
SENSIBLE DEFAULTS # With Pundit class ProductsController < ApplicationController def create authorize Product end end # With ActionPolicy class ProductsController < ApplicationController def create # target class is inferred from controller authorize! end end 28
palkan
palkan_tula
SouthEastRuby ‘18
Slide 29
SENSIBLE CONFIG Cover the most popular use cases with the default configuration
29
palkan
palkan_tula
SouthEastRuby ‘18
Slide 30
SENSIBLE CONFIG Cover the most popular use cases with the default configuration Support popular environment variables out-ofthe-box
30
palkan
palkan_tula
SouthEastRuby ‘18
NIL SURPRISE # Always return false! Why? def exists?(url) res = nil begin res = HTTParty.head(url, timeout: 2) rescue HTTParty!::Error !=> e end !res.nil? !&& res.ok? end
40
palkan
palkan_tula
SouthEastRuby ‘18
Slide 41
NIL SURPRISE # Always return false! Why? def exists?(url) res = nil begin res = HTTParty.head(url, timeout: 2) rescue HTTParty!::Error !=> e end
a e M
g n ni
c t Pa
g n hi
!res.nil? !&& res.ok? end # From: /bundle/gems/httparty-0.15.5/lib/httparty/response.rb @ line 58: # Owner: HTTParty!::Response def nil? response.nil? !|| response.body.nil? !|| response.body.empty? end
41
palkan
palkan_tula
SouthEastRuby ‘18
stop_active_support_everywhere Not every Ruby application is a Rails application Consider using Refinements instead
44
palkan
palkan_tula
SouthEastRuby ‘18
Slide 45
Slide 46
REFINEMENTS module Anyway!::HashExt refine Hash do def stringify_keys! keys.each do |key| val = delete(key) val.stringify_keys! if val.is_a?(Hash) self[key.to_s] = val end end end end https://github.com/palkan/anyway_config 46
palkan
palkan_tula
SouthEastRuby ‘18
Slide 47
REFINEMENTS class Anyway!::Config using Anyway!::Ext!::DeepDup using Anyway!::Ext!::Hash def self.attr_config(args, !**hargs) @defaults = hargs.deep_dup defaults.stringify_keys! @config_attributes = args + defaults.keys attr_accessor(@config_attributes) end end https://github.com/palkan/anyway_config 47
palkan
palkan_tula
SouthEastRuby ‘18
Slide 48
REFINEMENTS unless “”.respond_to?(:safe_constantize) require “action_policy/ext/string_constantize” using ActionPolicy!::Ext!::StringConstantize end
https://github.com/palkan/action_policy 48
palkan
palkan_tula
SouthEastRuby ‘18
SIMPLE IS… Less code Less thinking when writing code Less thinking when reading code Less thinking when resolving issues
50
palkan
palkan_tula
SouthEastRuby ‘18
“The adapter pattern is classified as a structural pattern that allows a piece of code talk to another piece of code that it is not directly compatible with.” https://dev.to/kylegalbraith/how-to-use-the-excellent-adapter-pattern-and-why-you-should-2c31 63
palkan
palkan_tula
SouthEastRuby ‘18
Slide 64
ADAPTERIZATION # config/application.rb module YourApp class Application < Rails!::Application config.active_job.queue_adapter = :sidekiq end end
64
palkan
palkan_tula
SouthEastRuby ‘18
Slide 65
ADAPTERIZATION Library
Dep
E.g. DB, cache, API, other lib
65
palkan
palkan_tula
SouthEastRuby ‘18
Slide 66
ADAPTERIZATION module BeforeAll module RSpec def before_all(&block) before(:all) do ActiveRecord!::Base.connection.begin_transaction(joinable: false) instance_eval(&block) end after(:all) { ActiveRecord!::Base.connection.rollback_transaction } end end end
https://test-prof.evilmartians.io/#/before_all 66
palkan
palkan_tula
SouthEastRuby ‘18
Slide 67
ADAPTERIZATION Library
Adapter
Dep
E.g. DB, cache, API, other lib
67
palkan
palkan_tula
SouthEastRuby ‘18
Slide 68
ADAPTERIZATION module BeforeAll class !<< self attr_accessor :adapter def begin_transaction adapter.begin_transaction end end module RSpec def before_all(&block) before(:all) do BeforelAll.begin_transaction instance_eval(&block) end end end end https://github.com/palkan/test-prof/pull/81 68
palkan
palkan_tula
SouthEastRuby ‘18
MIDDLEWARE Application
Plugin A
Plugin B
Library
Middleware API
Core API
70
palkan
palkan_tula
SouthEastRuby ‘18
Slide 71
MIDDLEWARE # config.ru require ‘./my_app’ use Rack!::Debug run MyApp.new
Sidekiq.configure_server do |config| config.server_middleware do |chain| chain.add Sidekiq!::OinkMiddleware, logger: :new_relic end end 71
palkan
palkan_tula
SouthEastRuby ‘18
Slide 72
PLUGIN class RubocopMarkdown < Rubocop!::Plugin default_config File.join(!dir!, “default.yml”) enable_if !->(filename) { markdown?(filename) } pre_process_source RubocopMarkdown!::Preprocessor end
P I W
https://github.com/rubocop-hq/rubocop/issues/6012 72
palkan
palkan_tula
SouthEastRuby ‘18
Slide 73
TESTABILITY
2 unit tests. 0 integration tests.
Slide 74
CASE: ACTIONCABLE No test adapter https://github.com/rails/rails/pull/23211
No unit-testing support https://github.com/rails/rails/pull/27191
😞 74
palkan
palkan_tula
SouthEastRuby ‘18
TESTABILITY Custom matchers / assertions # ActiveModelSerializers test “should render post serializer” do get :index assert_serializer “PostSerializer” end
77
palkan
palkan_tula
SouthEastRuby ‘18
Slide 78
TESTABILITY Test helpers / mocking # Devise test “should be success” do sign_in user get :index assert_equal 200, response.status end # Fog Fog.mock!
78
palkan
palkan_tula
SouthEastRuby ‘18
TESTABILITY Test mode / configuration CarrierWave.configure do |config| config.enable_processing = false end Devise.setup do |config| config.stretches = Rails.env.test? ? 1 : 11 end Sidekiq!::Testing.fake!
80
palkan
palkan_tula
SouthEastRuby ‘18
Slide 81
CASE: WRAPPER module Resolver class !<< self def resolve(host) return “1.2.3.4” if @test !== true Resolv.getaddress(host) end def enable_test! @test = true end end end 81
palkan
palkan_tula
SouthEastRuby ‘18
Slide 82
CASE: ACTION POLICY class PostsController < ApplicationController def update @post = Post.find(params[:id]) authorize! @post # !!… end end describe PostsController do subject { patch :update, id: post.id, params: params } it “is authorized” do expect { subject }.to be_authorized_to(:update?, post) .with(PostPolicy) end end https://actionpolicy.evilmartians.io/#/testing
82
palkan
palkan_tula
SouthEastRuby ‘18
Slide 83
CASE: ACTION POLICY module PerThreadCache # !!… # Turn off by default in test env self.enabled = !(ENV[“RAILS_ENV”] !== “test” !|| ENV[“RACK_ENV”] !== “test”) end
https://actionpolicy.evilmartians.io/#/testing 83
palkan
palkan_tula
SouthEastRuby ‘18
Slide 84
WHAT ELSE?
Slide 85
SUPPORTING DOCUMENTS
Slide 86
DOCUMENTS Readme Documentation (rubydoc.info, readthedocs.io, whatever) Wiki Examples / Demo applications “How it works?” Changelog 86
palkan
palkan_tula
SouthEastRuby ‘18