Skip to content

Commit

Permalink
Extract Assignable code to single module and refactor (#1483)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewmcgarvey committed Apr 26, 2021
1 parent 21edef8 commit 633c486
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 79 deletions.
12 changes: 12 additions & 0 deletions spec/lucky/assignable_spec.cr
Expand Up @@ -81,6 +81,16 @@ class OverrideGetterPage
end
end

class NonPageClass
include Lucky::Assignable

needs param : String
end

class InheritedNonPageClass < NonPageClass
needs other_param : String
end

describe "Assigns within multiple pages with the same name" do
it "should only appear once in the initializer" do
PageOne.new build_context, title: "foo", name: "Paul", second: "second"
Expand All @@ -91,5 +101,7 @@ describe "Assigns within multiple pages with the same name" do
PageWithMetaclass.new(build_context, string_class: String)
.perform_render.to_s.should contain("called from an auto-generated getter")
OverrideGetterPage.new(build_context).perform_render.to_s.should eq("Joe")
NonPageClass.new(param: "foo").param.should eq("foo")
InheritedNonPageClass.new(param: "foo", other_param: "bar").other_param.should eq("bar")
end
end
83 changes: 55 additions & 28 deletions src/lucky/assignable.cr
@@ -1,5 +1,5 @@
module Lucky::Assignable
# Declare what a page needs in order to be initialized.
# Declare what a class needs in order to be initialized.
#
# This will declare an instance variable and getter automatically. It will
# also add arguments to an `initialize` method at the end of compilation.
Expand All @@ -26,9 +26,6 @@ module Lucky::Assignable
{% unless declaration.is_a?(TypeDeclaration) %}
{% raise "'needs' expects a type declaration like 'name : String', instead got: '#{declaration}'" %}
{% end %}
{% if declaration.var.stringify.ends_with?("?") %}
{% raise "Using '?' in a 'needs' var name is no longer supported. Now Lucky generates a method ending in '?' if the type is 'Bool'." %}
{% end %}

# Ensure that the needs variable name has not been previously defined.
{% previous_declaration = ASSIGNS.find { |d| d.var == declaration.var } %}
Expand All @@ -42,44 +39,74 @@ module Lucky::Assignable
{% end %}

{% if declaration.type.stringify == "Bool" %}
def {{ declaration.var }}?
@{{ declaration.var }}
end
getter? {{ declaration.var }}
{% else %}
def {{ declaration.var }}
@{{ declaration.var }}
end
getter {{ declaration.var }}
{% end %}

{% ASSIGNS << declaration %}
{% end %}
end

# :nodoc:
macro included
SETTINGS = {} of Nil => Nil
ASSIGNS = [] of Nil

macro inherit_assigns
macro included
inherit_page_settings
inherit_assigns
end

macro inherited
inherit_page_settings
end
macro inherited
inherit_assigns
end

{% if !@type.has_constant?(:ASSIGNS) %}
ASSIGNS = [] of Nil
{% verbatim do %}
{% if @type.ancestors.first %}
{% for declaration in @type.ancestors.first.constant(:ASSIGNS) %}
{% ASSIGNS << declaration %}
{% end %}
{% end %}
{% end %}
{% end %}
end

# :nodoc:
macro inherit_page_settings
SETTINGS = {} of Nil => Nil
ASSIGNS = [] of Nil
macro setup_initializer_hook
macro finished
generate_needy_initializer
end

\{% for declaration in @type.ancestors.first.constant :ASSIGNS %}
\{% ASSIGNS << declaration %}
\{% end %}
macro included
setup_initializer_hook
end

macro inherited
setup_initializer_hook
end
end

\{% for k, v in @type.ancestors.first.constant :SETTINGS %}
\{% SETTINGS[k] = v %}
\{% end %}
macro generate_needy_initializer
{% if !@type.abstract? %}
{% sorted_assigns = ASSIGNS.sort_by { |dec|
has_explicit_value =
dec.type.is_a?(Metaclass) ||
dec.type.types.map(&.id).includes?(Nil.id) ||
!dec.value.is_a?(Nop)
has_explicit_value ? 1 : 0
} %}
def initialize(
{% for declaration in sorted_assigns %}
{% var = declaration.var %}
{% type = declaration.type %}
{% value = declaration.value %}
{% value = nil if type.stringify.ends_with?("Nil") && !value %}
@{{ var.id }} : {{ type }}{% if !value.is_a?(Nop) %} = {{ value }}{% end %},
{% end %}
**unused_exposures
)
end
{% end %}
end

setup_initializer_hook
inherit_assigns
end
46 changes: 1 addition & 45 deletions src/lucky/html_builder.cr
Expand Up @@ -21,51 +21,7 @@ module Lucky::HTMLBuilder
include Lucky::RenderIfDefined
include Lucky::TagDefaults

abstract def view

macro setup_initializer_hook
macro finished
generate_needy_initializer
end

macro included
setup_initializer_hook
end

macro inherited
setup_initializer_hook
end
end

macro included
setup_initializer_hook
end

macro generate_needy_initializer
{% if !@type.abstract? %}
{% sorted_assigns = ASSIGNS.sort_by { |dec|
has_explicit_value =
dec.type.is_a?(Metaclass) ||
dec.type.types.map(&.id).includes?(Nil.id) ||
dec.value ||
dec.value == nil ||
dec.value == false
has_explicit_value ? 1 : 0
} %}
def initialize(
{% for declaration in sorted_assigns %}
{% var = declaration.var %}
{% type = declaration.type %}
{% value = declaration.value %}
{% value = nil if type.stringify.ends_with?("Nil") && !value %}
{% has_default = value || value == false || value == nil %}
@{{ var.id }} : {{ type }}{% if has_default %} = {{ value }}{% end %},
{% end %}
**unused_exposures
)
end
{% end %}
end
abstract def view : IO

def perform_render : IO
render
Expand Down
9 changes: 4 additions & 5 deletions src/lucky/html_page.cr
@@ -1,15 +1,14 @@
require "./html_builder"

module Lucky::HTMLPage
include Lucky::HTMLBuilder

Habitat.create do
setting render_component_comments : Bool = false
end

macro included
include Lucky::HTMLBuilder
getter view = IO::Memory.new
needs context : HTTP::Server::Context
end
getter view : IO = IO::Memory.new
needs context : HTTP::Server::Context

def to_s(io)
io << view
Expand Down
2 changes: 1 addition & 1 deletion src/lucky/tags/form_helpers.cr
Expand Up @@ -5,7 +5,7 @@ module Lucky::FormHelpers

def form_for(route : Lucky::RouteHelper, **html_options) : Nil
form build_form_options(route, html_options) do
csrf_hidden_input if settings.include_csrf_tag
csrf_hidden_input if Lucky::FormHelpers.settings.include_csrf_tag
method_override_input(route)
yield
end
Expand Down

0 comments on commit 633c486

Please sign in to comment.