Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Or install it yourself as:
## Usage

```ruby
class class IndexPage < Tram::Page
class IndexPage < Tram::Page
# See dry-initializer
param :account
option :readonly, optional: true
Expand Down Expand Up @@ -52,6 +52,22 @@ IndexPage.new(Account.find(99)).to_h(except: :collection)
IndexPage.new(Account.find(99)).to_h(only: :collection)
```

Inheritance of page objects is supported:

```ruby
class FancyIndexPage < IndexPage
inherit_section :title
section :fancy_collection
inherit_section :index_url, if: :readonly_on?

def fancy_collection
collection.map(&:fancy)
end
end

FancyIndexPage.new(Account.find(99)).to_h # => { :title => "…", fancy_collection: […] }
```

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
Expand Down
20 changes: 15 additions & 5 deletions lib/tram/page.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# frozen_string_literal: true

require "dry-initializer"
require_relative "page/section"

class Tram::Page
extend ::Dry::Initializer

Expand All @@ -14,7 +17,14 @@ def section(name, options = {})

section = Section.new(name, options)
sections[name] = section
define_method(name, &section.block) if section.block
define_method(name, &section.value) if section.value
end

def inherit_section(name, option_overrides={})
name = name.to_sym
parent_section = superclass.sections[name]
options = Tram::Page::Section.dry_initializer.attributes(parent_section)
section(name, options.merge(option_overrides))
end

def url_helper(name)
Expand All @@ -28,10 +38,10 @@ def sections
end

def to_h(except: nil, only: nil, **)
obj = self.class.sections.values.map { |s| s.call(self) }.reduce({}, :merge)
obj = obj.reject { |k, _| Array(except).map(&:to_sym).include? k } if except
obj = obj.select { |k, _| Array(only).map(&:to_sym).include? k } if only
obj
sections = self.class.sections.dup
sections.select! { |k, _| Array(only).include? k } if only
sections.reject! { |k, _| Array(except).include? k } if except
sections.map { |_, section| section.call(self) }.reduce({}, :merge!)
end

private
Expand Down
24 changes: 15 additions & 9 deletions lib/tram/page/section.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@ class Tram::Page
class Section
extend Dry::Initializer
param :name, proc(&:to_sym)
option :method, proc(&:to_s), default: -> { name }, as: :method_name
option :value, proc(&:to_proc), optional: true, as: :block
option :if, proc(&:to_s), optional: true, as: :positive
option :unless, proc(&:to_s), optional: true, as: :negative
option :skip, true.method(:&), optional: true
option :method, proc(&:to_s), default: -> { name }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd better rename these methods because the original ones can provide problems during debugging
(but I agree to not renaming value to block)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not to mention using attributes adds a bunch of excessive steps in a runtime

option :value, proc(&:to_proc), optional: true
option :if, proc(&:to_s), optional: true
option :unless, proc(&:to_s), optional: true

# @param [Tram::Page] page
# @return [Hash] a part of the section
Expand All @@ -24,13 +23,20 @@ def call(page)
private

def skip_on?(page)
return true if skip
return true if positive && !page.public_send(positive)
return true if negative && page.public_send(negative)
return true if attributes[:if] && !page.__send__(attributes[:if])
return true if attributes[:unless] && page.__send__(attributes[:unless])
end

def value_at(page)
block ? page.instance_exec(&block) : page.public_send(method_name)
if attributes[:value]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value is already memoized, we don't need looking for it in a hash

page.instance_exec(&attributes[:value])
else
page.public_send(attributes[:method])
end
end

def attributes
@attributes ||= self.class.dry_initializer.attributes(self)
end
end
end
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "tram/page"
require "support/blank_page"
require "support/example_page"
require "support/inherited_page"

RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
Expand Down
15 changes: 15 additions & 0 deletions spec/support/inherited_page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

class InheritedPage < ExamplePage
inherit_section :foo
inherit_section :bar, if: :no_bar
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this DSL breaks the Less Surprise Principle. When I inherit a page from another one, I expect the children to have all the parent's sections (sections is the main responsibility for Tram::Page) without necessity to explicitly select them.

What I'd like is something like:

class InheritedPage < ExamplePage
  # section :foo is already here from `ExamplePage`
  section :bar, if: :no_bar, reload: true # tells explicitly: "I know that :bar is already here, and yes, I want to reload it"
  section :bam
end

@gzigzigzeo ?

section :bam

def bam
baz.upcase
end

def no_bar
!bar
end
end
16 changes: 16 additions & 0 deletions spec/tram/inherited_page_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe InheritedPage do
subject { described_class.new("test") }

it "returns data hash with new and without skipped fields" do
expect(subject.to_h).to eq(foo: "test", bam: "TEST")
end

it "doesn't change behaviour of the parent class" do
expect(described_class.superclass.new("test").to_h).to \
eq(bar: "test", foo: "test", baz: "test")
end
end