wrapper for the current phone validation library. module Phone WHITELIST = { "+3912312345678" => "+39 12312345678" } # Makes a plausibility check on the number. def self.plausible?(number) whitelisted?(number) || Phony.plausible?(number) end def self.format(number) whitelisted?(number) || Phony.formatted(Phony.normalize(number), parentheses: false) end
extensive change to the codebase • IncompaKbiliKes can be addressed in a single place • When and if needed, the external dependency can be replaced with liMle effort • TesKng doesn't require intensive stubbing
@adapter ||= NullAdapter.new end def resolve(name) adapter.resolve(name) end def enable_test! self.adapter = test_adapter.new end end # NullAdapter is a special adapter # that discards every resolve request. class NullAdapter def resolve(name) Result.new(name, [], []) end end class TestAdapter def resolve(*) result = @stubs.shift result ? Result.new(*result) : Result.new end end class GoAdapter BIN = File.join(Rails.root, "bin", "dsalias") def resolve(name) # end end
Models • Models MUST expose custom API to perform operaKons • Callbacks are allowed only for data integrity • Query methods in ActiveRecord::Base are not allowed outside the Models or Finders • Scopes can't be invoked directly outside Models or Finders and they exist only to support Finders
find_by_tld(identifier) end def self.find!(identifier) find(identifier) or not_found!(identifier) end def self.find_by_tld(identifier) tld = identifier.to_s.downcase enabled.where(tld: tld).take end def self.suffix_listing scope.enabled end def self.newgtld_suffixes scope.new_tlds.enabled.order_alpha end def self.gtld_suffixes scope.global_tlds.enabled.order_alpha end def self.cctld_suffixes scope.cc_tlds.enabled.order_alpha end def self.privacy_supported_tlds scope.where(whois_privacy: true).pluck(:tld) end private def self.not_found!(identifier) raise(ActiveRecord::RecordNotFound, "TLD `#{identifier}` not found") end end
def create(name, account, domain_attributes) ActiveRecord::Base.transaction do domain = Domain.new domain.create_domain(name, account, domain_attributes) zone_service = ZoneService.new zone_service.create(domain) end end def delete(domain) # ... end def lock(domain) # ... end end
method auto-assigns internal tokens used for hashing # and persists the object in the database. # # Returns true if the object is created, false otherwise. def create_domain(name, account, attributes = {}, options = {}) self.attributes = attributes self.name = self.class.nameize(name) self.account = account assign_token creating(options) do # ... end end # Changes the account ownership of this domain. # # Returns true if the object is updated, false otherwise. def move_to_account(account) update_attribute(:account_id, account.id) end end
# v2 mount Api::V2::App.new, at: '/v2' end # ... end Hanami::Router.define do get "/:account_id/contacts", to: "contacts#index", api: "listContacts" post "/:account_id/contacts", to: "contacts#create", api: "createContact" get "/:account_id/contacts/:contact_id", to: "contacts#show", api: "getContact" patch "/:account_id/contacts/:contact_id", to: "contacts#update", api: "updateContact" delete "/:account_id/contacts/:contact_id", to: "contacts#destroy", api: "deleteContact" get "/tlds", to: "tlds#index", api: "listTlds" get "/tlds/:tld_id", to: "tlds#show", api: "getTld" get "/whoami", to: "authentication_context#show", api: "whoami" post "/oauth/access_token", to: "oauths#access_token", api: "oauthToken" # ... end
this_account, DomainParams.new(params)) @domain = @result.data if @result.successful? render DomainSerializer.new(@domain), 201 else render ErrorSerializer.new(@result.error, @domain), end end end end class DomainHostingController def create @result = DomainCreateCommand.execute( command_context, this_account, domain_params) @domain = @result.data if @result.successful? redirect_to domain_path(@domain) else format.html { render action: "new" } end end end
:require_subscription!, :require_good_standing! def call(params) # code end end class Disable include Hanami::Action before :require_subscription! def call(params) # code end end end end
include Rendering include Errors include NotFoundHandler include Authentication include Subscription include Throttling def validate_account! (params[:account_id].present? && params[:account_id] != ACCOUNT_WILDCARD) or error(400, "Parameter `:account_id` is required") end def command_context actor = (authentication_context.user || authentication_context.account).to_actor Command::Context.new(actor: actor, authenticated: authentication_context) end end end end