class Htmlable a where to_html :: a - Html Sometimes more control is needed, for example rendering of some thing might be needed in two different contexts, with different rules. There are several options, including a subclass that takes additional context information, like this: class Htmlable a = HtmlableWithContext c a where to_html_c :: c - a - Html or we could wrap up our basic values into more complex types and get finer control via the more complex type’s instance definition, for (a contrived) example, to prefix the rendering of a value wrapped with strong tags with a number of stars, where we would call to_html (Stars "foo" 3) to get 3 stars in front of strongfoo/strong data Stars a = Stars { value :: a, num_stars :: Int } instance Htmlable a = Htmlable (Stars a) where to_html (Stars v n) = text (take n $ repeat '*') + strong (to_html a) Similarly, we can control rendering of collections by wrapping them in a different type, where the type’s instance applies the relevant rendering of the container. data Unordered a = Unordered { collection :: [a] } instance Htmlable a = Htmlable (Unordered a) where to_html (Unordered xs) = ul map (li . to_html) xs Notice: we’re using types again (as wrappers) to represent the concepts we want, rather than passing around some ad hoc flags, and this can help to ensure consistency and avoid silly mistakes. As a final example, we can use a similar mechanism to define table formats, perhaps storing a list of values together with a list of (column title, extraction function) pairs that control how the columns are rendered. The extraction functions could themselves return Htmlable values so that relevant rendering of links or plain data can be handled. Forms and Updates This is one of the parts of Rails that make me nervous, particularly when a form should only update part of an object. All that hassle with protected fields, nested attributes, etc. In light of the previous discussion, my suggestion is to capture the relevant user story as part of the app’s interface description (that is, as a type), and generate the form and the handling code from this. So in the context of viewing a certain list, if (say) we wanted to edit its title and date, then the interface description would contain this as a possible step for a given list: ... | ChangeTitleAndDate String Date And the interpreter for this would (depending on context) either yield a form for changing those fields, or perform the relevant update. As with routes, it may be possible to avoid the usual string encoding of fields of nested attributes PragPub January 2013 14
Previous Page Next Page