# WrapIt
This library provides set of classes and modules with simple DSL for quick and easy creating html helpers with your own DSL. It's usefull for implementing CSS frameworks, or making your own.
For example, your designer makes perfect button style for some site. This element will appears in many places of site in some variations. The button have `danger`, `success` and `default` look, and can have `active` state. Button can have some icon. So, you make some CSS styles, and now you should place HTML markup of this element in many places of site. With `wrap_it` library you can do it with following code:
```ruby
WrapIt.register_module Helpers
module Helpers
class PerfectButton < WrapIt::Container
include TextContainer
html_class 'button'
enum :look, [:default, :success, :danger], html_class_prefix: 'button-'
switch :active, html_class: 'button-active'
child :icon, [tag: 'img', class: 'button-icon']
end
register :p_button, 'PerfectButton'
end
```
Now, include this helper into you template engine. For Rails:
```ruby
class MyController < ApplicationController
helper Helpers
...
end
```
And you can use it in you ERB:
```html
<%= p_button %>button 1<% end %>
<%= p_button 'button 2', :active, :success %>
<%= p_button active: true, look: :success, body: 'button 3' %>
<%= p_button :danger do |b| %>
<%= b.icon src: '/path/to/icon.png' %>
button 4
<% end %>
```
This will produce following code:
```html
button text
```
## WrapIt::Base
### DSL methods
#### default_tag(name)
Use `default_tag` DSL method inside your class to specify HTML tag name for element. This tag can be changed soon by you or user. `name` can be `Symbol` or `String` and it converted to `String`.
#### html_class(*args)
Use `html_class` DSL method to add default html classes, thats are automatically added when element created.
#### html_class_prefix(prefix)
Sets html class prefix. It can be `Symbol` or `String` and converted to `String`. This value used with `switch` and `enum` functionality. See its descriptions below.
#### omit_content
Once this method called from class, this class will ommit any text content, captured from template. For example, `<%= element do %>
Any content
<% end %>` normally will produce `
`. In some cases you whant to drop `
Any content
`, for exmaple, inside tables.
#### switch(name, options = {}, &block)
Adds `switch`. Switch is a boolean flag. When element created, creation arguments will be scanned for `Symbol`, that equals to `name`. If it founded, switch turned on. Also creation options inspected. If its contains `name: true` key-value pair, this pair removed from options and switch also turned on. `name` can be `Symbol` or `String` and it converted to `Symbol`.
This method also adds getter and setter for this switch in form `name?` and `name=` respectively.
When `html_class` option specified and switch changes its state, HTML class for element will be computed as follows. if `html_class` options is `true`, html class produced from `html_class_prefix` and `name` of switch. If `html_class` is a String, Symbol or Array of this types, html class produced as array of `html_class_prefix` and each `html_class` concatinations. This classes added to element if switch is on or removed in other case.
Also `aliases` option available. So if some of aliases founded in arguments it also changes switch state. You should pass only `Symbol` or `Array` if symbols to this optioin.
If block given, it will be called each time switch changes its state in context of element with the switch state as argument. If you return `false` from this block, value is ommited.
#### enum(name, options = {}, &block)
Adds `enum`. When element created, creation arguments will be scanned for `Symbol`, that included contains in `values`. If it founded, enum takes this value. Also creation options inspected. If its contains `name: value` key-value pair with valid value, this pair removed from options and enum takes this value.
This method also adds getter and setter for this enum.
If you set `html_class` option to `true`, with each enum change, HTML class, composed from `html_class_prefix` and enum `value` will be added to element. If you want to override this prefix, specify it with `html_class_prefix` option. By default, enum changes are not affected to html classes.
Also `aliases` option available. So if some of aliases founded in creation options keys it also changes enum value. You should pass only `Symbol` or `Array` if symbols to this optioin.
`default` option sets default value for enum. This value will used if nil or invalid value assigned to enum.
If block given, it will be called each time enum changes its value in context of element with the new value as argument.
#### section(*args)
Adds one ore more sections to element. Refer to [Sections explained](https://github.com/cybernetlab/wrap_it/blob/master/sections_explained.md) article for description.
#### place(src, dst)
Places section `src` to destination, specified in `dst` hash. `dst` is a single key-value Hash. Key can be `:before` and `:after`. Value can be `:begin`, `:end` or any section name. Refer to [Sections explained](https://github.com/cybernetlab/wrap_it/blob/master/sections_explained.md) article for description.
#### sections
Returns list of all sections, including derived from parent classes.
#### placement
Returns placed sections.
### Instance methods
#### self[name] and self[name]=
Retrieves or sets `name` section. Refer to [Sections explained](https://github.com/cybernetlab/wrap_it/blob/master/sections_explained.md) article for description.
#### wrap(*args, &block)
Wraps element with another.
You can provide wrapper directly or specify wrapper class as first argument. In this case wrapper will created with specified set of arguments and options. If wrapper class ommited, WrapIt::Base will be used.
If block present, it will be called when wrapper will rendered.
#### html_class
Returns array of html classes
#### html_class=(*args)
Sets html class(es) for element. Arguments can be `String`, `Symbol` or `Array` of it. All converted to plain array of `Symbols`. Duplicated classes removed.
#### add_html_class(*args)
Adds html class. For args see `#html_class=`
#### remove_html_class(*args)
Removes html class. For args see `#html_class=`
#### html_class?(*args, &block)
Determines whether element contains class, satisfied by conditions, specified in method arguments.
There are two forms of method call: with list of conditions as arguments and with block for comparing. Method makes comparison with html class untill first `true` return value or end of list. All conditions should be satisfied for `true` return of this method.
In first form, each argument treated as condition. Condition can be a `Regexp`, so html classes of element tested for matching to that regular expression. If condition is an `Array` then every class will be tested for presence in this array. If condition is `Symbol` or `String` classes will be compared with it via equality operator `==`.
In second form all arguments are ignored and for each comparison given block called with html class as argument. Block return value then used.
*Examples*
```ruby
# with `Symbol` or `String` conditions
element.html_class = [:a, :b, :c]
element.html_class?(:a) #=> true
element.html_class?(:d) #=> false
element.html_class?(:a, 'b') #=> true
element.html_class?(:a, :d) #=> false
# with `Regexp` conditions
element.html_class = [:some, :test]
element.html_class?(/some/) #=> true
element.html_class?(/some/, /bad/) #=> false
element.html_class?(/some/, :test) #=> true
# with `Array` conditions
element.html_class = [:a, :b, :c]
element.html_class?(%w(a d)) #=> true
element.html_class?(%w(e d)) #=> false
# with block
element.html_class = [:a, :b, :c]
element.html_class? { |x| x == 'a' } #=> true
```
#### no_html_class?(*args, &block)
Determines whether element doesn't contains class, satisfied by conditions, specified in method arguments. See `html_class?`.
#### set_html_data(name, value)
Sets HTML data attribute named `name` to `value`.
#### remove_html_data(name)
Removes HTML data attribute named `name`.
## WrapIt::Container
This class used for elements, that can hold other elements.
At first, note, that children can be created by two ways. First - inside template and second - from code. If child created from template, you have two choises again: first to keep child in place, where it defined in template, second - to cut it from there and place together with other childs. Two variables affects on this process: `ommit_content`, defined in `WrapIt::Base` and `extarct_children`. If `ommit_content` is `true`, all content will be dropped and children placed inside `children` section. If `extract_children` is true, children also placed into `children` section, but `content` is keeped.
At second, you have two choises of moment, when children rendered. If `deffered_render` set to `false`, that is as default, all children will be rendered immideately after creation, so you can't change any of them later. If you plan to render your container after children, you chould set it to `true`, so childrens are collected in buffer and will be rendered with parent.
All children, added to container injected with `render_to` and `parent` methods, that are gives you rendering section name and container itself.
### DSL methods
#### child(name, *args, &block)
Creates your own DSL method to create child items. In arguments, you should specify name of method. Then you can specify class name or class itself for child. If ommited, `WrapIt::Base` will be used. All other arguments will be mixed with arguments, specified by user and passed to child constructor. **Warning!** Make shure, that your child arguments don't begins with `String` if you ommit class argument. As workaround, don't ommit class argument and specify it as 'WrapIt::Base'. At last, you can define block, that will be called after creating child, but before its rendering. This child passed as argument to block.
You can render children to section, other than `:children`. To do this, specify `section` option with section name.
Look into [lib/bootstrap_it/view_helpers](https://github.com/cybernetlab/bootstrap_it/tree/master/lib/bootstrap_it/view_helpers) folder for usage examples.
# Todo
* Enlarge library functionality
* Strong testing
* Finish Sinatra integration
* Rubydoc documentation
# Changes
`0.2.0`
* added: sections mechanism
* many fixes
* testing improvement
* preparing testing for multiple frameworks
`0.1.5`
* fixed: switches and enums can damage instance variables
* fixed: process helper_name option before initialize callbacks
* fixed: convert user defined tag to string
* added: Link class
`0.1.4`
* added: html_class_prefix
`0.1.3`
* this is a fix for 0.1.2, that it was not properly compiled.
`0.1.2`
* fixed: double callbacks inclusion issue
* added: Base#wrap
* added: HTML data attribute processing
* test improvements
`0.1.1`
* WrapIt.helpers fails if no helper registered
`0.1.0`
* initial version
# Testing
This package developed for different frameworks, so testing is not so simple. At first, prepare testing environment with:
```sh
bundle install
bundle install --gemfile Gemfile.rails4
bundle install --gemfile Gemfile.sinatra
```
And then you can run tests as follows:
```sh
FRAMEWORK=rails4 bundle exec rake spec
FRAMEWORK=sinatra bundle exec rake spec
```
As sinatra support is in progress, its test will not pass yet.
# License
The MIT License (MIT)
Copyright (c) 2014 Alexey Ovchinnikov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.