Module: Presentability
- Extended by:
- Loggability
- Defined in:
- lib/presentability.rb
Overview
Facade-based presenter toolkit with minimal assumptions.
## Basic Usage
Basic usage of Presentability requires two steps: declaring presenters and then using them.
### Declaring Presenters
Presenters are just regular Ruby classes with some convenience methods for declaring exposures, but in a lot of cases you’ll want to declare them all in one place. Presentability offers a mixin that implements a simple DSL for declaring presenters and their associations to entity classes, intended to be used in a container module:
require 'presentability'
module Acme::Presenters
extend Presentability
presenter_for( Acme::Widget ) do
expose :sku
expose :name
expose :unit_price
end
end
The block of ‘presenter_for` is evaluated in the context of a new Presenter class, so refer to that documentation for what’s possible there.
Sometimes you can’t (or don’t want to) have to load the entity class to declare a presenter for it, so you can also declare it using the class’s name:
presenter_for( 'Acme::Widget' ) do
expose :sku
expose :name
expose :unit_price
end
### Using Presenters
You use presenters by instantiating them with the object they are a facade for (the “subject”), and then applying it:
= Acme::Widget.new(
sku: "FF-2237H455",
name: "Throbbing Frobnulator",
unit_price: 299,
inventory_count: 301,
wholesale_cost: 39
)
presentation = Acme::Presenters.present( )
# => { :sku => "FF-2237H455", :name => "Throbbing Frobnulator", :unit_price => 299 }
If you want to present a collection of objects as a collection, you can apply presenters to the collection instead:
= Acme::Widget.where { inventory_count > 0 }
collection_presentation = Acme::Presenters.present_collection( )
# => [ {:sku => "FF-2237H455", [...]}, {:sku => "FF-2237H460", [...]}, [...] ]
The collection can be anything that is ‘Enumerable`.
### Presentation Options
Sometimes you want a bit more flexibility in what you present, allowing a single uniform presenter to be used in multiple use cases. To facilitate this, you can pass an options keyword hash to ‘#present`:
presenter_for( 'Acme::Widget' ) do
expose :sku
expose :name
expose :unit_price
# Only expose the wholesale cost if presented via an internal API
expose :wholesale_cost, if: :internal_api
end
= Acme::Widget.new(
sku: "FF-2237H455",
name: "Throbbing Frobnulator",
unit_price: 299,
inventory_count: 301,
wholesale_cost: 39
)
# External API remains unchanged:
presentation = Acme::Presenters.present( )
# => { :sku => "FF-2237H455", :name => "Throbbing Frobnulator", :unit_price => 299 }
# But when run from an internal service:
internal_presentation = Acme::Presenters.present( , internal_api: true )
# => { :sku => "FF-2237H455", :name => "Throbbing Frobnulator", :unit_price => 299,
# :wholesale_cost => 39 }
There are some options that are set for you:
<dl> <td>:in_collection
</td> <dd>Set if the current object is being presented as part of a collection.</dd> </dl>
Defined Under Namespace
Classes: Presenter
Constant Summary collapse
- VERSION =
Package version
'0.6.0'
Class Method Summary collapse
-
.extended(mod) ⇒ Object
Extension hook – decorate the including
mod
.
Instance Method Summary collapse
-
#present(object, **options) ⇒ Object
Return a representation of the
object
by applying a declared presentation. -
#present_collection(collection, **options) ⇒ Object
Return an Array of all representations of the members of the
collection
by applying a declared presentation. -
#presenter_for(entity_class, &block) ⇒ Object
Set up a presentation for the given
entity_class
. -
#serialize(object) ⇒ Object
Serialize the specified
object
if a serializer has been declared for it and return the scalar result. -
#serialize_array(object) ⇒ Object
Default serializer for an Array; returns a new array of presented objects.
-
#serialize_hash(object) ⇒ Object
Default serializer for a Hash; returns a new Hash of presented keys and values.
-
#serializer_for(type, method) ⇒ Object
Set up a rule for how to serialize objects of the given
type
if there is no presenter declared for it.
Class Method Details
.extended(mod) ⇒ Object
Extension hook – decorate the including mod
.
132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/presentability.rb', line 132 def self::extended( mod ) super mod.singleton_class.attr_accessor :presenters mod.singleton_class.attr_accessor :serializers mod.presenters = {} mod.serializers = { Array => mod.method( :serialize_array ), Hash => mod.method( :serialize_hash ), } end |
Instance Method Details
#present(object, **options) ⇒ Object
Return a representation of the object
by applying a declared presentation.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/presentability.rb', line 171 def present( object, ** ) representation = self.present_by_class( object, ** ) || self.present_by_classname( object, ** ) || self.serialize( object, ** ) unless representation if object.instance_variables.empty? return object else raise NoMethodError, "no presenter found for %p" % [ object ], caller( 1 ) end end return representation end |
#present_collection(collection, **options) ⇒ Object
Return an Array of all representations of the members of the collection
by applying a declared presentation.
190 191 192 193 |
# File 'lib/presentability.rb', line 190 def present_collection( collection, ** ) = .merge( in_collection: true ) return collection.map {|object| self.present(object, **) } end |
#presenter_for(entity_class, &block) ⇒ Object
Set up a presentation for the given entity_class
.
151 152 153 154 155 156 |
# File 'lib/presentability.rb', line 151 def presenter_for( entity_class, &block ) presenter_class = Class.new( Presentability::Presenter ) presenter_class.module_eval( &block ) self.presenters[ entity_class ] = presenter_class end |
#serialize(object) ⇒ Object
Serialize the specified object
if a serializer has been declared for it and return the scalar result.
198 199 200 201 202 203 204 205 |
# File 'lib/presentability.rb', line 198 def serialize( object, ** ) serializer = self.serializers[ object.class ] || self.serializers[ object.class.name ] or return nil serializer_proc = serializer.to_proc return serializer_proc.call( object ) end |
#serialize_array(object) ⇒ Object
Default serializer for an Array; returns a new array of presented objects.
209 210 211 212 213 |
# File 'lib/presentability.rb', line 209 def serialize_array( object ) return object.map do |member| self.present( member, in_collection: true ) end end |
#serialize_hash(object) ⇒ Object
Default serializer for a Hash; returns a new Hash of presented keys and values.
217 218 219 220 221 222 223 224 |
# File 'lib/presentability.rb', line 217 def serialize_hash( object ) return object.each_with_object( {} ) do |(key, val), newhash| p_key = self.present( key, in_collection: true ) p_val = self.present( val, in_collection: true ) newhash[ p_key ] = p_val end end |
#serializer_for(type, method) ⇒ Object
Set up a rule for how to serialize objects of the given type
if there is no presenter declared for it.
161 162 163 |
# File 'lib/presentability.rb', line 161 def serializer_for( type, method ) self.serializers[ type ] = method end |