{"id":1324,"date":"2025-09-23T13:45:37","date_gmt":"2025-09-23T16:45:37","guid":{"rendered":"https:\/\/acsiv.com.br\/blog\/?p=1324"},"modified":"2025-09-23T14:11:22","modified_gmt":"2025-09-23T17:11:22","slug":"camadas-declarativas-um-padrao-flexivel-para-ruby","status":"publish","type":"post","link":"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/","title":{"rendered":"Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby"},"content":{"rendered":"\n<p id=\"c70c\">Em&nbsp;<strong>Ruby<\/strong>&nbsp;, os blocos s\u00e3o uma das ferramentas mais elegantes e expressivas \u00e0 nossa disposi\u00e7\u00e3o. Eles permitem que m\u00e9todos recebam comportamento como argumento e ajudam a manter o c\u00f3digo conciso e leg\u00edvel. De estruturas como&nbsp;<code>each<\/code>,&nbsp;<code>map<\/code>e&nbsp;<code>File.open<\/code>at\u00e9 bibliotecas como&nbsp;<strong>RSpec<\/strong>&nbsp;e&nbsp;<strong>Rails<\/strong>&nbsp;, os blocos s\u00e3o essenciais para o estilo de programa\u00e7\u00e3o&nbsp;<strong>Ruby<\/strong>&nbsp;.<\/p>\n\n\n\n<p id=\"96ae\">Qualquer pessoa familiarizada com&nbsp;<strong>Ruby<\/strong>&nbsp;certamente j\u00e1 se deparou com um bloco em algum momento. Mas antes de prosseguir, uma r\u00e1pida explica\u00e7\u00e3o sobre o que exatamente \u00e9 um bloco e por que ele \u00e9 t\u00e3o \u00fatil no ecossistema&nbsp;<strong>Ruby<\/strong>&nbsp;. Sinta-se \u00e0 vontade para pular para o pr\u00f3ximo cap\u00edtulo se voc\u00ea j\u00e1 se sentir confort\u00e1vel em us\u00e1-lo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"a5d5\">O que \u00e9 um bloco<\/h2>\n\n\n\n<p id=\"5692\">Um bloco \u00e9 um peda\u00e7o de c\u00f3digo que pode ser passado como par\u00e2metro e executado em um contexto diferente daquele em que foi declarado. Em Ruby, podemos invoc\u00e1-lo usando o&nbsp;<code>yield<\/code>m\u00e9todo e at\u00e9 mesmo passar argumentos para ele. Se o bloco for opcional, o&nbsp;<code>block_given?<\/code>m\u00e9todo nos permite verificar com seguran\u00e7a se um bloco foi fornecido.<\/p>\n\n\n\n<p id=\"10b1\">Tamb\u00e9m podemos capturar explicitamente o bloco usando&nbsp;<code>&amp;block<\/code>, o que permite que ele seja tratado como um&nbsp;<code>Proc<\/code>objeto, facilitando sua reutiliza\u00e7\u00e3o ou encaminhamento para outros m\u00e9todos.<\/p>\n\n\n\n<p id=\"dd39\">Vejamos alguns exemplos abaixo:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">def  greetings<br>   puts \"Oi!\" <br>  yield  if block_given? <br>  puts \"Tchau!\" <br>end<br><br> greetings do<br>   puts \"Como vai voc\u00ea?\" <br>end <br><br># =&gt; <br># Oi! <br># Como vai voc\u00ea? <br># Tchau! <br><br>def  execute_two_times ( &amp;block ) <br>  block.call <br>  block.call <br>end<br><br> execute_two_times do<br>   puts \"Chamando o bloco!\" <br>end <br><br># =&gt; <br># Chamando o bloco! <br># Chamando o bloco!<\/pre>\n\n\n\n<p id=\"c077\">Com esses exemplos simples, podemos ver como&nbsp;<strong>Ruby<\/strong>&nbsp;pode ser altamente expressivo, abrindo caminho para alguns recursos bem&nbsp;<strong>DRY<\/strong>&nbsp;(Don&#8217;t Repeat Yourself). Ao longo dos anos trabalhando com Ruby, fiz uso extensivo de blocos em&nbsp;<code>helpers<\/code>,&nbsp;<code>services<\/code>,&nbsp;<code>concerns<\/code>&#8230; E a lista continua.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"cb83\">Camada Declarativa<\/h2>\n\n\n\n<p id=\"31fc\">Com o tempo, usando&nbsp;<strong>o Rails<\/strong>&nbsp;, sempre me impressionei com a eleg\u00e2ncia com que a&nbsp;<strong>gem&nbsp;<\/strong><strong>ActsAsTenant<\/strong>&nbsp;permite definir o&nbsp;<strong>tenant<\/strong>&nbsp;com base no dom\u00ednio ou subdom\u00ednio. Com uma \u00fanica linha, declaramos a l\u00f3gica que vincula a solicita\u00e7\u00e3o ao&nbsp;<strong>tenant<\/strong>&nbsp;correto :<strong><\/strong><strong><\/strong><strong><\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">definir_locat\u00e1rio_atual_por_subdom\u00ednio_ou_dom\u00ednio( :conta , :subdom\u00ednio , :dom\u00ednio )<\/pre>\n\n\n\n<p id=\"b92b\">Este m\u00e9todo \u00e9 chamado diretamente dentro do&nbsp;<code>controller<\/code>, lado a lado com m\u00e9todos cl\u00e1ssicos como&nbsp;<code>before_action<\/code>ou&nbsp;<code>helper_method<\/code>, e implementa um comportamento altamente espec\u00edfico \u2014 mas de forma t\u00e3o fluida, t\u00e3o perfeitamente integrado ao ambiente&nbsp;<strong>Rails<\/strong>&nbsp;, que quase parece um comando nativo do pr\u00f3prio framework. \u00c9 uma daquelas constru\u00e7\u00f5es que exemplifica perfeitamente o que veio a ser conhecido como o&nbsp;<strong>Estilo Rails<\/strong>&nbsp;: simples, leg\u00edvel e incrivelmente poderoso.<\/p>\n\n\n\n<p id=\"7ac9\">Essa sutileza me inspira h\u00e1 muito tempo. Sempre tive a esperan\u00e7a de um dia criar algo que tivesse essa mesma eleg\u00e2ncia \u2014 uma maneira de encapsular comportamentos complexos por meio de uma interface declarativa, direta e natural.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"2986\"><strong>Primeira tentativa<\/strong><\/h2>\n\n\n\n<p id=\"7eff\">Minha primeira tentativa de criar algo t\u00e3o elegante quanto a defini\u00e7\u00e3o&nbsp;<strong>de tenant<\/strong>&nbsp;surgiu da necessidade de filtrar algumas consultas por meio de par\u00e2metros&nbsp;<strong>de URL<\/strong>&nbsp;, e faz\u00ea-lo com comportamento condicional e expressividade. A ideia principal era permitir que os escopos fossem ativados automaticamente quando determinados par\u00e2metros fossem usados. Deveria ser usado da seguinte forma:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">classe  MyController &lt; ApplicationController<br>   add_scope :type_a<br>   add_scope :type_b<br>   add_scope :type_c , if: -&gt; { condi\u00e7\u00e3o? } <br><br>  # ... <br>fim<\/pre>\n\n\n\n<p id=\"e269\">Se a URL contiver os termos-chave&nbsp;<code>type_a<\/code>,&nbsp;<code>type_b<\/code>e&nbsp;<code>type_c<\/code>e as condi\u00e7\u00f5es correspondentes (se houver) forem avaliadas como verdadeiras, o escopo apropriado ser\u00e1 aplicado automaticamente \u00e0 consulta. Para atingir esse comportamento, implementei um&nbsp;<code>concern<\/code>que estende os m\u00e9todos de classe de qualquer modelo que o inclua:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">m\u00f3dulo MyConcern <br>  estender ActiveSupport::Concern <br><br>  def  self .included(base) <br>    base.class_eval do<br>       estender ClassMethods <br>      class_attribute :scopes <br>      self .scopes = [] <br>    fim <br>  fim <br><br>  m\u00f3dulo ClassMethods <br>    def  add_scope ( escopo, **op\u00e7\u00f5es ) <br>      escopos &lt;&lt; { escopo: , **op\u00e7\u00f5es } <br>    fim <br>  fim <br><br>  # Implementa\u00e7\u00e3o do recurso de escopo que \u00e9 irrelevante por enquanto... <br>fim<\/pre>\n\n\n\n<p id=\"6fa8\">A implementa\u00e7\u00e3o acima cria um&nbsp;<code>concern<\/code>que, quando inclu\u00eddo por uma classe (representada pelo&nbsp;<code>base<\/code>par\u00e2metro), adiciona comportamentos a essa classe via&nbsp;<code>class_eval<\/code>. A linha&nbsp;<code>extend ClassMethods<\/code>significa que o&nbsp;<code>ClassMethods<\/code>m\u00f3dulo fornecer\u00e1&nbsp;<strong>m\u00e9todos de classe<\/strong>&nbsp;\u2014 em outras palavras, m\u00e9todos que podem ser chamados diretamente na classe, em vez de em suas inst\u00e2ncias.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\">\n<p id=\"09b1\">O nome&nbsp;<code>ClassMethods<\/code>\u00e9 apenas uma conven\u00e7\u00e3o amplamente utilizada na comunidade&nbsp;<strong>Rails<\/strong>&nbsp;; qualquer outro nome tamb\u00e9m funcionaria. Na verdade, ao estender&nbsp;<code>ActiveSupport::Concern<\/code>, podemos simplificar essa defini\u00e7\u00e3o usando o&nbsp;<code>class_methods<\/code>bloco, que encapsula m\u00e9todos de classe de uma forma mais elegante e concisa.<\/p>\n<\/blockquote>\n\n\n\n<p id=\"c26c\">Isso&nbsp;<code>concern<\/code>define uma lista chamada&nbsp;<code>scopes<\/code>e um m\u00e9todo&nbsp;<code>add_scope<\/code>, que adiciona elementos a essa lista de forma simples e declarativa. Este atributo reside no&nbsp;<strong>escopo da classe<\/strong>&nbsp;, o que traz uma vantagem importante em contextos como&nbsp;controladores&nbsp;<strong>Rails .<\/strong><\/p>\n\n\n\n<p id=\"d1e5\">Em uma aplica\u00e7\u00e3o web, cada requisi\u00e7\u00e3o HTTP (como uma chamada REST) \u200b\u200bresulta na cria\u00e7\u00e3o de uma nova inst\u00e2ncia do controlador \u2014 ou seja,&nbsp;<strong>os objetos que manipulam as requisi\u00e7\u00f5es n\u00e3o compartilham o estado entre si<\/strong>&nbsp;. Isso significa que quaisquer dados armazenados em vari\u00e1veis \u200b\u200bou m\u00e9todos de inst\u00e2ncia s\u00e3o&nbsp;<strong>transit\u00f3rios<\/strong>&nbsp;, existindo apenas durante a execu\u00e7\u00e3o daquela requisi\u00e7\u00e3o espec\u00edfica.<\/p>\n\n\n\n<p id=\"4b21\">Atributos de classe (como&nbsp;<code>self.scopes<\/code>), por outro lado, residem no&nbsp;<strong>escopo da pr\u00f3pria classe<\/strong>&nbsp;, sendo compartilhados entre todas as inst\u00e2ncias. Isso permite que configura\u00e7\u00f5es ou declara\u00e7\u00f5es feitas por meio dela&nbsp;<code>add_scope<\/code>sejam persistentemente registradas e acess\u00edveis por qualquer inst\u00e2ncia futura do controlador.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"32ec\">Segunda Tentativa<\/h2>\n\n\n\n<p id=\"f326\">Minha segunda tentativa surgiu da necessidade de atribuir dinamicamente comportamento estruturado a diferentes classes&nbsp;<strong>ActiveModel<\/strong>&nbsp;de forma elegante (de prefer\u00eancia). O uso pretendido seria algo assim:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Classe MyModel &lt; MyRootModel <br>  define_slots do tipo<br>     de slot : :a , requer: \"MySubClassOfTypeA\" tipo     de slot : :a tipo de     slot : :b fim fim  <br> <br> <br>  <\/pre>\n\n\n\n<p id=\"accc\">A ideia aqui \u00e9 que o&nbsp;<strong>modelo<\/strong>&nbsp;tenha tr\u00eas slots: dois do tipo&nbsp;<code>a<\/code>e um do tipo&nbsp;<code>b<\/code>, e um dos slots do tipo&nbsp;<code>a<\/code>deve necessariamente conter um objeto da classe&nbsp;<code>MySubClassOfTypeA<\/code>.<\/p>\n\n\n\n<p id=\"2a10\">Meu objetivo era obter um&nbsp;<em>hash<\/em>&nbsp;estruturado com os slots dispon\u00edveis para cada&nbsp;<strong>modelo<\/strong>&nbsp;, e a implementa\u00e7\u00e3o foi a seguinte:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">classe  MyRootModel <br>  classe &lt;&lt; auto <br>    def  define_slots ( &amp;block ) <br>      @slot_rules = [] <br><br>      dsl = Classe .new do <br>        def  inicializar ( slots ) <br>          @slots = slots <br>        fim <br><br>        def  slot ( **kwargs ) <br>          @slots &lt;&lt; kwargs <br>        fim <br>      fim<br><br>       dsl.new( @slot_rules ).instance_eval(&amp;block) <br>    fim <br>  fim <br>fim<\/pre>\n\n\n\n<p id=\"b888\">Esta implementa\u00e7\u00e3o demonstra um uso mais sofisticado de blocos. O m\u00e9todo&nbsp;<code>define_slots<\/code>recebe um bloco e o executa dentro do contexto de uma classe interna, criada exclusivamente para interpretar aquele dom\u00ednio espec\u00edfico. O comando&nbsp;<code>slot<\/code>por si s\u00f3 n\u00e3o tem significado no escopo externo \u2014 ele n\u00e3o pertence a&nbsp;<code>ActiveRecord<\/code>ou a&nbsp;<code>MyRootModel<\/code>. Mas, quando interpretado dentro de uma inst\u00e2ncia dessa classe an\u00f4nima, ele ganha contexto e prop\u00f3sito. Assim, um simples bloco se transforma em uma linguagem declarativa espec\u00edfica do dom\u00ednio, com seu pr\u00f3prio vocabul\u00e1rio. E a melhor parte: estender essa &#8220;linguagem&#8221; \u00e9 t\u00e3o simples quanto adicionar um novo m\u00e9todo \u00e0 classe DSL.<\/p>\n\n\n\n<p id=\"da31\">Com essa abordagem, qualquer&nbsp;<strong>modelo<\/strong>&nbsp;que herde de&nbsp;<code>MyRootModel<\/code>pode definir seus slots usando&nbsp;<code>define_slots<\/code>e acessar as regras declaradas via&nbsp;<code>@slot_rules<\/code>. Em algum momento, refatorei essa l\u00f3gica \u2014 extraindo a classe an\u00f4nima para seu pr\u00f3prio arquivo e dando uma estrutura mais robusta aos&nbsp;<code>slot<\/code>argumentos (substituindo o uso direto de&nbsp;<code>**kwargs<\/code>) \u2014 mas isso est\u00e1 al\u00e9m do escopo atual. A vers\u00e3o apresentada aqui j\u00e1 \u00e9 suficiente para demonstrar a ideia central: como o uso consciente de blocos pode permitir peda\u00e7os elegantes e reutiliz\u00e1veis, perfeitamente alinhados com a expressividade do Ruby.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"b2b1\">Terceira Tentativa<\/h2>\n\n\n\n<p id=\"aa2f\">Minha terceira e mais recente tentativa tamb\u00e9m foi a que mais me entusiasmou. No meu trabalho na Acsiv, os dados necess\u00e1rios para um recurso \u00e0s vezes variam entre os contextos. Um bom exemplo \u00e9 o cadastro de indiv\u00edduos. Dependendo da associa\u00e7\u00e3o, s\u00e3o necess\u00e1rios atributos diferentes.<\/p>\n\n\n\n<p id=\"dfff\">Por lei, o registro do&nbsp;<strong>requerente<\/strong>&nbsp;de um ato notarial deve incluir nome, e-mail e n\u00famero de telefone, enquanto o registro&nbsp;<strong>de funcion\u00e1rio<\/strong>&nbsp;n\u00e3o exige informa\u00e7\u00f5es de contato. A situa\u00e7\u00e3o fica ainda mais curiosa quando falamos de certid\u00f5es:<\/p>\n\n\n\n<ul>\n<li>para uma&nbsp;<strong>certid\u00e3o de nascimento<\/strong>&nbsp;, precisamos da data de nascimento;<\/li>\n\n\n\n<li>para uma&nbsp;<strong>certid\u00e3o de \u00f3bito<\/strong>&nbsp;, precisamos da data do \u00f3bito.<\/li>\n<\/ul>\n\n\n\n<p id=\"365a\">Portanto, temos um&nbsp;<code>model<\/code>que est\u00e1 associado a muitos outros, e&nbsp;<strong>a valida\u00e7\u00e3o de seus atributos depende de qual deles est\u00e1 relacionado<\/strong>&nbsp;. A solu\u00e7\u00e3o \u00f3bvia seria definir as valida\u00e7\u00f5es dentro do&nbsp;<code>model<\/code>pr\u00f3prio objeto, mas, nesse caso, ele precisaria saber a que est\u00e1 relacionado, e o c\u00f3digo ficaria mais ou menos assim:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">valida :email , presen\u00e7a:  true , se: -&gt; { personable.is_a?(Request) } <br>valida :date_of_birth , presen\u00e7a:  true , se: -&gt; { personable.is_a?(Birth) } <br>valida :date_of_death , presen\u00e7a:  true , se: -&gt; { personable.is_a?(Death) }<\/pre>\n\n\n\n<p id=\"6edb\">Diga ol\u00e1 ao\u00a0<strong>alto acoplamento<\/strong>\u00a0e\u00a0<strong>\u00e0 baixa coes\u00e3o<\/strong>\u00a0&#x1f60a;<\/p>\n\n\n\n<p id=\"30cc\">E como bons seguidores do&nbsp;<em>C\u00f3digo Limpo<\/em>&nbsp;do&nbsp;<strong>Tio Bob<\/strong>&nbsp;, sabemos que n\u00e3o \u00e9 assim que deveria ser:<em><\/em><\/p>\n\n\n\n<blockquote class=\"wp-block-quote\">\n<p id=\"8031\">\u201cC\u00f3digo limpo \u00e9 c\u00f3digo com alta coes\u00e3o e baixo acoplamento.\u201d<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"0ca9\">A solu\u00e7\u00e3o:<code>validates_association<\/code><\/h2>\n\n\n\n<p id=\"2ffe\">Para resolver o problema da forma mais elegante poss\u00edvel e sem violar os princ\u00edpios de design,&nbsp;<code>validates_association<\/code>surgiu a ideia de criar. \u00c9 muito f\u00e1cil: permitir que a&nbsp;<strong>classe que define a rela\u00e7\u00e3o<\/strong>&nbsp;(&nbsp;<code>Birth<\/code>,&nbsp;<code>Death<\/code>, etc.) tamb\u00e9m&nbsp;<strong>defina as valida\u00e7\u00f5es de associa\u00e7\u00e3o<\/strong>&nbsp;, exatamente como faria com&nbsp;<code>validates<\/code>, mas dentro de um bloco personalizado. Exemplo:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">classe  Nascimento<br>   tem_um :pessoa<br>   valida_associa\u00e7\u00e3o :pessoa  faz<br>     valida :data_de_nascimento , presen\u00e7a:  verdadeiro <br>  fim <br>fim <br><br>classe  Morte<br>   tem_um :pessoa<br>   valida_associa\u00e7\u00e3o :pessoa  faz<br>     valida :data_de_morte , presen\u00e7a:  verdadeiro <br>  fim <br>fim<\/pre>\n\n\n\n<p id=\"17ac\">Veja abaixo como podemos criar o m\u00e9todo:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">m\u00f3dulo AssociationValidatable <br>  estender ActiveSupport::Concern <br><br>  class_methods do <br>    def  validates_association ( associa\u00e7\u00e3o, &amp;bloco ) <br>      reflection = reflect_on_association(associa\u00e7\u00e3o) <br>      klass = reflection.klass <br>      foreign_key = reflection.type <br><br>      dsl = Class .new do <br>        def  initialize ( klass, foreign_key, refer\u00eancia ) <br>          @klass        = klass <br>          @foreign_key = foreign_key <br>          @reference    = refer\u00eancia <br>        fim   <br><br>        def  validates ( *atributos, **op\u00e7\u00f5es ) <br>          fk = @foreign_key<br>           ref = @reference<br><br>           condi\u00e7\u00e3o = -&gt; { atributos[fk] == ref } <br>          op\u00e7\u00f5es[ :if ] = condi\u00e7\u00e3o <br><br>          @klass .validates(*atributos, **op\u00e7\u00f5es) <br>        fim <br>      fim<br><br>       dsl.new(klass, foreign_key, nome).instance_eval(&amp;bloco) <br>    fim <br>  fim <br>fim<\/pre>\n\n\n\n<p id=\"7f97\">Com essa implementa\u00e7\u00e3o, a ideia de permitir que a classe que define o relacionamento tamb\u00e9m defina as valida\u00e7\u00f5es dessa associa\u00e7\u00e3o \u2014 desde que a associa\u00e7\u00e3o aponte para ela \u2014 torna-se realidade. Observe que aqui usamos&nbsp;<code>class_methods<\/code>, como explicado anteriormente. Por meio dele,&nbsp;<code>models<\/code>os include&nbsp;<code>AssociationValidatable<\/code>obt\u00eam acesso ao&nbsp;<code>validates_association<\/code>m\u00e9todo. Esse m\u00e9todo, por sua vez, instancia uma&nbsp;<strong>classe an\u00f4nima<\/strong>&nbsp;(como vimos na segunda tentativa), que define um&nbsp;<code>validates<\/code>m\u00e9todo com a mesma assinatura da&nbsp;<code>ActiveModel<\/code>vers\u00e3o original \u2014 e, por fim, delega a chamada a ele.<\/p>\n\n\n\n<p id=\"9f65\">Voc\u00ea pode ter notado as vari\u00e1veis&nbsp;<code>fk<\/code>\u200b\u200be&nbsp;<code>ref<\/code>e se perguntado por que elas s\u00e3o usadas. Isso \u00e9 necess\u00e1rio devido ao modo como&nbsp;<strong>os closures (lambdas\/procs)<\/strong>&nbsp;funcionam em&nbsp;<strong>Ruby<\/strong>&nbsp;. Vari\u00e1veis \u200b\u200bde inst\u00e2ncia como&nbsp;<code>@foreign_key<\/code>e&nbsp;<code>@reference<\/code>s\u00e3o avaliadas&nbsp;<strong>no contexto do objeto onde o lambda \u00e9 executado, n\u00e3o onde foi definido<\/strong>&nbsp;. Em outras palavras, o bloco&nbsp;<code>-&gt; { attributes[@foreign_key] == @reference }<\/code>seria executado dentro da inst\u00e2ncia de associa\u00e7\u00e3o \u2014 e, como essa inst\u00e2ncia&nbsp;<strong>n\u00e3o possui<\/strong>&nbsp;as vari\u00e1veis&nbsp;<code>@foreign_key<\/code>\u200b\u200be&nbsp;<code>@reference<\/code>, o resultado seria sempre&nbsp;<code>nil<\/code>. Ao atribuir esses valores a&nbsp;<strong>vari\u00e1veis \u200b\u200blocais<\/strong>&nbsp;(&nbsp;<code>fk<\/code>,&nbsp;<code>ref<\/code>), garantimos que seus valores sejam&nbsp;<strong>capturados corretamente pelo closure<\/strong>&nbsp;, j\u00e1 que lambdas em Ruby capturam o escopo local no momento da defini\u00e7\u00e3o.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\">\n<p id=\"4d9b\">N\u00e3o se esque\u00e7a de reiniciar o servidor! Essas altera\u00e7\u00f5es afetam a defini\u00e7\u00e3o da classe, que s\u00f3 \u00e9 carregada durante a inicializa\u00e7\u00e3o.<\/p>\n<\/blockquote>\n\n\n\n<p id=\"b228\">Dito isto, ainda podemos melhorar nossa implementa\u00e7\u00e3o de v\u00e1rias maneiras, como:<\/p>\n\n\n\n<ul>\n<li>Cria\u00e7\u00e3o de&nbsp;<em>a\u00e7\u00facar sint\u00e1tico<\/em>&nbsp;para simplificar c\u00f3digo repetitivo;<\/li>\n\n\n\n<li>Permitir que a associa\u00e7\u00e3o valide&nbsp;<strong>suas pr\u00f3prias associa\u00e7\u00f5es<\/strong>&nbsp;;<\/li>\n\n\n\n<li>Tornando as valida\u00e7\u00f5es&nbsp;<strong>condicionais<\/strong>&nbsp;, se necess\u00e1rio.<\/li>\n<\/ul>\n\n\n\n<p id=\"6956\">Dentre elas, a cria\u00e7\u00e3o do&nbsp;<em>a\u00e7\u00facar sint\u00e1tico<\/em>&nbsp;\u00e9 a mais simples \u2014 ent\u00e3o vamos come\u00e7ar com isso.<\/p>\n\n\n\n<p id=\"cf64\">A ideia aqui \u00e9 permitir que voc\u00ea use uma valida\u00e7\u00e3o predefinida de forma clara e concisa, como neste exemplo:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">validates_association :pessoa , como:  :solicitante<\/pre>\n\n\n\n<p id=\"20d4\">O primeiro passo \u00e9 definir uma predefini\u00e7\u00e3o chamada&nbsp;<code>:requester<\/code>. Declarei o bloco dentro da associa\u00e7\u00e3o&nbsp;<code>model<\/code>,&nbsp;<code>Person<\/code>neste caso.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">classe  Pessoa <br>  VALIDATION_PRESETS = { <br>    solicitante:  proc  do<br>       valida :doc_cpf_cnpj , presen\u00e7a:  true<br>       valida :email , presen\u00e7a:  true<br>       valida :phone_number , presen\u00e7a:  true <br>    fim<br>   } <br>fim<\/pre>\n\n\n\n<p id=\"3176\">No peda\u00e7o acima,&nbsp;<code>VALIDATION_PRESETS<\/code>h\u00e1 apenas uma constante padr\u00e3o&nbsp;<strong>do Ruby<\/strong>&nbsp;, o que significa que podemos acess\u00e1-la com&nbsp;<code>Person::VALIDATION_PRESETS<\/code>.<\/p>\n\n\n\n<p id=\"fc2d\">Agora, vamos adaptar nosso c\u00f3digo para que ele possa responder a um&nbsp;<code>as:<\/code>par\u00e2metro e encontrar a predefini\u00e7\u00e3o correta:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">m\u00f3dulo AssociationValidatable <br>  extend ActiveSupport::Concern <br><br>  class_methods do <br>    def  validates_association ( association, as:  nil , &amp;block ) <br>      reflection = reflect_on_association(association) <br>      klass = reflection.klass <br>      foreign_key = reflection.type <br>      preset = options[ :as ] <br><br>      dsl = Class .new do <br>        def  initialize ( klass, foreign_key, reference ) <br>          @klass             = klass <br>          @foreign_key       = foreign_key <br>          @reference         = reference <br>        end <br><br>        def  validates ( *attrs, **options ) <br>          fk = @foreign_key<br>           ref = @reference<br><br>           condition = -&gt; { attributes[fk] == ref } <br>          options[ :if ] = condition <br><br>          @klass .validates(*attrs, **options) <br>        end <br>      end<br><br>       block | |= klass:: VALIDATION_PRESETS [preset] <br><br>      raise ArgumentError, \"Nem bloco nem predefini\u00e7\u00e3o foram fornecidos.\"  if block. nil ? <br><br>      dsl.new(klass, chave_estrangeira, nome).instance_eval(&amp;bloco) <br>    fim <br>  fim <br>fim<\/pre>\n\n\n\n<p id=\"b6e2\">Com essa mudan\u00e7a,&nbsp;<code>validates_association<\/code>agora suporta:<\/p>\n\n\n\n<ul>\n<li>um tradicional&nbsp;<code>block<\/code>, com valida\u00e7\u00f5es declaradas em linha;<\/li>\n\n\n\n<li>uma&nbsp;<code>as:<\/code>op\u00e7\u00e3o que aponta para uma predefini\u00e7\u00e3o previamente definida no associado&nbsp;<code>model<\/code>.<\/li>\n<\/ul>\n\n\n\n<p id=\"9592\">Esta pequena adi\u00e7\u00e3o aumenta significativamente a flexibilidade e ajuda a manter o&nbsp;<code>models<\/code>ambiente mais limpo e coeso.<\/p>\n\n\n\n<p id=\"3cab\">Agora, vamos aos pontos restantes.<\/p>\n\n\n\n<p id=\"a1c1\">Imagine que&nbsp;<code>Birth<\/code>est\u00e1 relacionado a&nbsp;<code>Person<\/code>, e&nbsp;<code>Person<\/code>, por sua vez, est\u00e1 relacionado a&nbsp;<code>Address<\/code>. Queremos permitir&nbsp;<code>Birth<\/code>a valida\u00e7\u00e3o n\u00e3o apenas&nbsp;<code>Person<\/code>dos campos de , mas tamb\u00e9m daqueles de&nbsp;<code>Address<\/code>quando existe um relacionamento entre eles.<\/p>\n\n\n\n<p id=\"2772\">O que nos leva \u00e0 seguinte quest\u00e3o:&nbsp;<strong>como validamos uma associa\u00e7\u00e3o de terceiro n\u00edvel?<\/strong><\/p>\n\n\n\n<p id=\"1763\">Com essa abordagem, \u00e9 bem simples! Precisamos apenas que nossa classe an\u00f4nima tenha seu pr\u00f3prio&nbsp;<code>validates_association<\/code>m\u00e9todo \u2014 um que saiba como acessar a associa\u00e7\u00e3o interna, validar sua estrutura e permitir a especifica\u00e7\u00e3o de condi\u00e7\u00f5es personalizadas.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">m\u00f3dulo AssociationValidatable <br>  estender ActiveSupport::Concern <br><br>  class_methods do <br>    def  validates_association ( associa\u00e7\u00e3o, **op\u00e7\u00f5es, &amp;bloco ) <br>      reflection = reflect_on_association(associa\u00e7\u00e3o) <br>      klass = reflection.klass <br>      foreign_key = reflection.type <br><br>      preset = options[ :as ] <br>      if_condition = options[ :if ] <br>      unless _condition = options[ :unless ] <br><br>      dsl = Class .new do <br>        def  initialize ( klass, foreign_key, refer\u00eancia, if_condition, unless _condition ) <br>          @klass             = klass <br>          @foreign_key       = foreign_key <br>          @reference         = refer\u00eancia <br>          @if_condition      = if_condition | | -&gt; { true } <br>          @unless_condition = unless _condition | | -&gt; { false } <br>        fim <br><br>        def  valida ( *attrs, **op\u00e7\u00f5es ) <br>          fk = @foreign_key<br>           ref = @reference<br>           if_condition = @if_condition <br>          unless _condition = @unless_condition<br>           condi\u00e7\u00e3o = -&gt; { atributos[fk] == ref &amp;&amp; instance_exec(&amp;if_condition) } <br><br>          se op\u00e7\u00f5es[ :if ] <br>            existente = op\u00e7\u00f5es[ :if ] <br>            op\u00e7\u00f5es[ :if ] = -&gt; { inst\u00e2ncia_exec(&amp;existente) &amp;&amp; inst\u00e2ncia_exec(&amp;condi\u00e7\u00e3o) } <br>          sen\u00e3o<br>             op\u00e7\u00f5es[ :if ] = condi\u00e7\u00e3o <br>          fim <br><br>          se op\u00e7\u00f5es[ :unless ] <br>            existente = op\u00e7\u00f5es[ :unless ] <br>            op\u00e7\u00f5es[ :unless ] = -&gt; { inst\u00e2ncia_exec(&amp;existente) | | instance_exec(&amp; unless _condition) } <br>          else<br>             options[ :unless ] = unless _condition <br>          end <br><br>          @klass .validates(*attrs, **options) <br>        end <br><br>        def  validates_association ( associa\u00e7\u00e3o, **op\u00e7\u00f5es, &amp;bloco ) <br>          reflection = @klass .reflect_on_association(associa\u00e7\u00e3o)<br>          fk = @foreign_key<br>           ref = @reference<br>           preset = options[ :as ] <br>          unless _condition = options[ :unless ] <br><br>          if_condition = if options[ :if ] <br>                           existing = options[ :if ] <br>                           -&gt; { instance_exec(&amp;existing) &amp;&amp; public_send(reflection.options[ :as ]).public_send(fk) == ref } <br>                         else<br>                            -&gt; { public_send(reflection.options[ :as ]).public_send(fk) == ref } <br>                         end <br><br>          @klass .validates_association(association, preset: , if: if_condition, unless:  unless _condition, &amp;block) <br>        end <br>      end<br><br>       block | |= klass:: VALIDATION_PRESETS [preset] <br><br>      raise ArgumentError, \"Nem bloco nem predefini\u00e7\u00e3o foram fornecidos.\"  if block. nil ? <br><br>      dsl.new(klass, chave_estrangeira, nome, condi\u00e7\u00e3o_se, a menos que_condi\u00e7\u00e3o).instance_eval(&amp;bloco) <br>    fim <br>  fim <br>fim<\/pre>\n\n\n\n<p id=\"bcf8\">O mais importante \u00e9 entender que&nbsp;<strong>fechamentos (lambdas\/procs)<\/strong>&nbsp;s\u00e3o executados no contexto da&nbsp;<code>model<\/code>valida\u00e7\u00e3o, portanto, precisamos garantir que o relacionamento entre todos os objetos na cadeia esteja definido corretamente. Em nosso exemplo,&nbsp;<code>Address<\/code>deve ser associado a&nbsp;<code>Person<\/code>, e este&nbsp;<code>Person<\/code>, por sua vez, deve ser associado corretamente a&nbsp;<code>Birth<\/code>para que as valida\u00e7\u00f5es definidas em&nbsp;<code>Birth<\/code>sejam aplicadas \u00e0s associa\u00e7\u00f5es aninhadas.<\/p>\n\n\n\n<p id=\"4ec3\">Com isso em mente, agora podemos us\u00e1-lo conforme mostrado nos exemplos a seguir:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">  validates_association :pessoa  do<br>     validates :data_de_nascimento , presen\u00e7a:  verdadeiro<br><br>     validates_association :endere\u00e7o  do<br>       validates :rua , presen\u00e7a:  verdadeiro <br>    fim <br>  fim<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">  validates_association :pessoa  faz<br>     valida :data_de_nascimento , presen\u00e7a:  verdadeiro , se: -&gt; {condi\u00e7\u00e3o?} <br><br>    validates_association :endere\u00e7o  faz<br>       valida :rua , presen\u00e7a:  verdadeiro , a menos que: -&gt; {outra_condi\u00e7\u00e3o?} <br>    fim <br>  fim<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">  valida_associa\u00e7\u00e3o :pessoa , se: -&gt; {condi\u00e7\u00e3o?} faz<br>     valida :data_de_nascimento , presen\u00e7a:  verdadeiro<br><br>     valida_associa\u00e7\u00e3o :endere\u00e7o , a menos que: -&gt; {outra_condi\u00e7\u00e3o?} faz<br>       valida :rua , presen\u00e7a:  verdadeiro <br>    fim <br>  fim<\/pre>\n\n\n\n<p id=\"b592\">O que chamo de&nbsp;<strong>Camada Declarativa<\/strong>&nbsp;\u00e9 o resultado final ao qual cheguei ap\u00f3s tentar extrair todo o potencial dos&nbsp;<em>blocos<\/em>&nbsp;. N\u00f3s os utilizamos para organizar o comportamento de forma clara e modular, focando mais na declara\u00e7\u00e3o do que na l\u00f3gica em si. Em vez de definir regras ou l\u00f3gica diretamente dentro dos m\u00e9todos, encapsulamos essas responsabilidades em camadas bem definidas que podem ser facilmente compostas, reutilizadas e testadas.<\/p>\n\n\n\n<p id=\"f778\">Explorar camadas declarativas revela um dos aspectos mais poderosos do\u00a0<strong>Ruby<\/strong>\u00a0: a capacidade de transformar c\u00f3digo em linguagem. Em vez de impor estruturas r\u00edgidas ou acoplamentos complexos, criamos espa\u00e7os onde a sem\u00e2ntica de dom\u00ednio assume o controle. O resultado s\u00e3o interfaces leg\u00edveis e extens\u00edveis que se adaptam ao problema que estamos modelando \u2014 e n\u00e3o o contr\u00e1rio. Essas camadas podem ser consideradas pequenas\u00a0<strong>DSLs<\/strong>\u00a0, permitindo que voc\u00ea crie seus pr\u00f3prios contextos. No final, o que antes parecia &#8220;apenas um bloco com um limite&#8221; torna-se uma base s\u00f3lida para projetos mais expressivos e sustent\u00e1veis.<\/p>\n\n\n\n<p class=\"has-text-align-right\">Escrito por: Fillipe Palhares<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Esse artigo explora o uso de blocos em Ruby para criar camadas declarativas, que tornam o c\u00f3digo mais expressivo, leg\u00edvel e reutiliz\u00e1vel. Atrav\u00e9s de exemplos pr\u00e1ticos em Rails, o autor mostra como blocos permitem construir DSLs elegantes, reduzir acoplamento, aumentar coes\u00e3o e transformar l\u00f3gica complexa em interfaces simples e flex\u00edveis. Leia o artigo completo!<\/p>\n","protected":false},"author":2,"featured_media":1326,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[15,23,8],"tags":[1016,1012,1014,1017,1011,1013,1010,835,1009,1015],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\r\n<title>&quot;Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby&quot;<\/title>\r\n<meta name=\"description\" content=\"Aprenda como blocos em Ruby criam camadas declarativas e DSLs elegantes, deixando o c\u00f3digo mais limpo, leg\u00edvel, flex\u00edvel em projetos Rails.\" \/>\r\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\r\n<link rel=\"canonical\" href=\"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/\" \/>\r\n<meta property=\"og:locale\" content=\"pt_BR\" \/>\r\n<meta property=\"og:type\" content=\"article\" \/>\r\n<meta property=\"og:title\" content=\"&quot;Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby&quot;\" \/>\r\n<meta property=\"og:description\" content=\"Aprenda como blocos em Ruby criam camadas declarativas e DSLs elegantes, deixando o c\u00f3digo mais limpo, leg\u00edvel, flex\u00edvel em projetos Rails.\" \/>\r\n<meta property=\"og:url\" content=\"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/\" \/>\r\n<meta property=\"og:site_name\" content=\"Blog da Acsiv\" \/>\r\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/acsiv\" \/>\r\n<meta property=\"article:published_time\" content=\"2025-09-23T16:45:37+00:00\" \/>\r\n<meta property=\"article:modified_time\" content=\"2025-09-23T17:11:22+00:00\" \/>\r\n<meta property=\"og:image\" content=\"https:\/\/acsiv.com.br\/blog\/wp-content\/uploads\/2025\/09\/altumcode-oZ61KFUQsus-unsplash-scaled.jpg\" \/>\r\n\t<meta property=\"og:image:width\" content=\"2048\" \/>\r\n\t<meta property=\"og:image:height\" content=\"2560\" \/>\r\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\r\n<meta name=\"author\" content=\"Eduardo Rodrigues\" \/>\r\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\r\n<meta name=\"twitter:creator\" content=\"@acsiv\" \/>\r\n<meta name=\"twitter:site\" content=\"@acsiv\" \/>\r\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"Eduardo Rodrigues\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. tempo de leitura\" \/>\n\t<meta name=\"twitter:data2\" content=\"17 minutos\" \/>\r\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https:\/\/acsiv.com.br\/blog\/#organization\",\"name\":\"Acsiv Sistema\",\"url\":\"https:\/\/acsiv.com.br\/blog\/\",\"sameAs\":[\"https:\/\/instagram.com\/acsivsistemas\",\"https:\/\/pt.linkedin.com\/company\/acsiv\",\"https:\/\/www.facebook.com\/acsiv\",\"https:\/\/twitter.com\/acsiv\"],\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/acsiv.com.br\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/acsiv.com.br\/blog\/wp-content\/uploads\/2020\/10\/Acsiv_logotipo.png\",\"contentUrl\":\"https:\/\/acsiv.com.br\/blog\/wp-content\/uploads\/2020\/10\/Acsiv_logotipo.png\",\"width\":1564,\"height\":668,\"caption\":\"Acsiv Sistema\"},\"image\":{\"@id\":\"https:\/\/acsiv.com.br\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/acsiv.com.br\/blog\/#website\",\"url\":\"https:\/\/acsiv.com.br\/blog\/\",\"name\":\"Blog da Acsiv\",\"description\":\"Conte\u00fado digital para cart\u00f3rios\",\"publisher\":{\"@id\":\"https:\/\/acsiv.com.br\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/acsiv.com.br\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"pt-BR\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/\",\"url\":\"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/\",\"name\":\"\\\"Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby\\\"\",\"isPartOf\":{\"@id\":\"https:\/\/acsiv.com.br\/blog\/#website\"},\"datePublished\":\"2025-09-23T16:45:37+00:00\",\"dateModified\":\"2025-09-23T17:11:22+00:00\",\"description\":\"Aprenda como blocos em Ruby criam camadas declarativas e DSLs elegantes, deixando o c\u00f3digo mais limpo, leg\u00edvel, flex\u00edvel em projetos Rails.\",\"breadcrumb\":{\"@id\":\"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/#breadcrumb\"},\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"In\u00edcio\",\"item\":\"https:\/\/acsiv.com.br\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby\"}]},{\"@type\":\"Article\",\"@id\":\"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/\"},\"author\":{\"name\":\"Eduardo Rodrigues\",\"@id\":\"https:\/\/acsiv.com.br\/blog\/#\/schema\/person\/54fd566d0efeb6dc731df7e4e38ce156\"},\"headline\":\"Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby\",\"datePublished\":\"2025-09-23T16:45:37+00:00\",\"dateModified\":\"2025-09-23T17:11:22+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/\"},\"wordCount\":2406,\"publisher\":{\"@id\":\"https:\/\/acsiv.com.br\/blog\/#organization\"},\"keywords\":[\"#CleanCode\",\"#C\u00f3digoLimpo\",\"#Declarativo\",\"#DesenvolvimentoDeSoftware\",\"#DesenvolvimentoWeb\",\"#DSL\",\"#Programa\u00e7\u00e3o\",\"#Rails\",\"#Ruby\",\"#RubyOnRails\"],\"articleSection\":[\"Cart\u00f3rios\",\"Dicas\",\"Tecnologia\"],\"inLanguage\":\"pt-BR\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/acsiv.com.br\/blog\/#\/schema\/person\/54fd566d0efeb6dc731df7e4e38ce156\",\"name\":\"Eduardo Rodrigues\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/acsiv.com.br\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/fee13940cf1da650036b52bc338d5881?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/fee13940cf1da650036b52bc338d5881?s=96&d=mm&r=g\",\"caption\":\"Eduardo Rodrigues\"}}]}<\/script>\r\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"\"Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby\"","description":"Aprenda como blocos em Ruby criam camadas declarativas e DSLs elegantes, deixando o c\u00f3digo mais limpo, leg\u00edvel, flex\u00edvel em projetos Rails.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/","og_locale":"pt_BR","og_type":"article","og_title":"\"Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby\"","og_description":"Aprenda como blocos em Ruby criam camadas declarativas e DSLs elegantes, deixando o c\u00f3digo mais limpo, leg\u00edvel, flex\u00edvel em projetos Rails.","og_url":"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/","og_site_name":"Blog da Acsiv","article_publisher":"https:\/\/www.facebook.com\/acsiv","article_published_time":"2025-09-23T16:45:37+00:00","article_modified_time":"2025-09-23T17:11:22+00:00","og_image":[{"width":2048,"height":2560,"url":"https:\/\/acsiv.com.br\/blog\/wp-content\/uploads\/2025\/09\/altumcode-oZ61KFUQsus-unsplash-scaled.jpg","type":"image\/jpeg"}],"author":"Eduardo Rodrigues","twitter_card":"summary_large_image","twitter_creator":"@acsiv","twitter_site":"@acsiv","twitter_misc":{"Escrito por":"Eduardo Rodrigues","Est. tempo de leitura":"17 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Organization","@id":"https:\/\/acsiv.com.br\/blog\/#organization","name":"Acsiv Sistema","url":"https:\/\/acsiv.com.br\/blog\/","sameAs":["https:\/\/instagram.com\/acsivsistemas","https:\/\/pt.linkedin.com\/company\/acsiv","https:\/\/www.facebook.com\/acsiv","https:\/\/twitter.com\/acsiv"],"logo":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/acsiv.com.br\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/acsiv.com.br\/blog\/wp-content\/uploads\/2020\/10\/Acsiv_logotipo.png","contentUrl":"https:\/\/acsiv.com.br\/blog\/wp-content\/uploads\/2020\/10\/Acsiv_logotipo.png","width":1564,"height":668,"caption":"Acsiv Sistema"},"image":{"@id":"https:\/\/acsiv.com.br\/blog\/#\/schema\/logo\/image\/"}},{"@type":"WebSite","@id":"https:\/\/acsiv.com.br\/blog\/#website","url":"https:\/\/acsiv.com.br\/blog\/","name":"Blog da Acsiv","description":"Conte\u00fado digital para cart\u00f3rios","publisher":{"@id":"https:\/\/acsiv.com.br\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/acsiv.com.br\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"pt-BR"},{"@type":"WebPage","@id":"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/","url":"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/","name":"\"Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby\"","isPartOf":{"@id":"https:\/\/acsiv.com.br\/blog\/#website"},"datePublished":"2025-09-23T16:45:37+00:00","dateModified":"2025-09-23T17:11:22+00:00","description":"Aprenda como blocos em Ruby criam camadas declarativas e DSLs elegantes, deixando o c\u00f3digo mais limpo, leg\u00edvel, flex\u00edvel em projetos Rails.","breadcrumb":{"@id":"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/#breadcrumb"},"inLanguage":"pt-BR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"In\u00edcio","item":"https:\/\/acsiv.com.br\/blog\/"},{"@type":"ListItem","position":2,"name":"Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby"}]},{"@type":"Article","@id":"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/#article","isPartOf":{"@id":"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/"},"author":{"name":"Eduardo Rodrigues","@id":"https:\/\/acsiv.com.br\/blog\/#\/schema\/person\/54fd566d0efeb6dc731df7e4e38ce156"},"headline":"Camadas Declarativas: Um Padr\u00e3o Flex\u00edvel para Ruby","datePublished":"2025-09-23T16:45:37+00:00","dateModified":"2025-09-23T17:11:22+00:00","mainEntityOfPage":{"@id":"https:\/\/acsiv.com.br\/blog\/2025\/09\/23\/camadas-declarativas-um-padrao-flexivel-para-ruby\/"},"wordCount":2406,"publisher":{"@id":"https:\/\/acsiv.com.br\/blog\/#organization"},"keywords":["#CleanCode","#C\u00f3digoLimpo","#Declarativo","#DesenvolvimentoDeSoftware","#DesenvolvimentoWeb","#DSL","#Programa\u00e7\u00e3o","#Rails","#Ruby","#RubyOnRails"],"articleSection":["Cart\u00f3rios","Dicas","Tecnologia"],"inLanguage":"pt-BR"},{"@type":"Person","@id":"https:\/\/acsiv.com.br\/blog\/#\/schema\/person\/54fd566d0efeb6dc731df7e4e38ce156","name":"Eduardo Rodrigues","image":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/acsiv.com.br\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/fee13940cf1da650036b52bc338d5881?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/fee13940cf1da650036b52bc338d5881?s=96&d=mm&r=g","caption":"Eduardo Rodrigues"}}]}},"_links":{"self":[{"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/posts\/1324"}],"collection":[{"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/comments?post=1324"}],"version-history":[{"count":1,"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/posts\/1324\/revisions"}],"predecessor-version":[{"id":1325,"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/posts\/1324\/revisions\/1325"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/media\/1326"}],"wp:attachment":[{"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/media?parent=1324"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/categories?post=1324"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/acsiv.com.br\/blog\/wp-json\/wp\/v2\/tags?post=1324"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}