formular 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +29 -0
  4. data/CHANGELOG.md +3 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +105 -0
  8. data/Rakefile +12 -0
  9. data/formular.gemspec +33 -0
  10. data/lib/formular.rb +8 -0
  11. data/lib/formular/attributes.rb +45 -0
  12. data/lib/formular/builder.rb +52 -0
  13. data/lib/formular/builders/basic.rb +64 -0
  14. data/lib/formular/builders/bootstrap3.rb +28 -0
  15. data/lib/formular/builders/bootstrap3_horizontal.rb +32 -0
  16. data/lib/formular/builders/bootstrap3_inline.rb +10 -0
  17. data/lib/formular/builders/bootstrap4.rb +36 -0
  18. data/lib/formular/builders/bootstrap4_horizontal.rb +39 -0
  19. data/lib/formular/builders/bootstrap4_inline.rb +15 -0
  20. data/lib/formular/builders/foundation6.rb +28 -0
  21. data/lib/formular/element.rb +135 -0
  22. data/lib/formular/element/bootstrap3.rb +70 -0
  23. data/lib/formular/element/bootstrap3/checkable_control.rb +105 -0
  24. data/lib/formular/element/bootstrap3/column_control.rb +45 -0
  25. data/lib/formular/element/bootstrap3/horizontal.rb +146 -0
  26. data/lib/formular/element/bootstrap3/input_group.rb +83 -0
  27. data/lib/formular/element/bootstrap4.rb +49 -0
  28. data/lib/formular/element/bootstrap4/checkable_control.rb +94 -0
  29. data/lib/formular/element/bootstrap4/custom_control.rb +120 -0
  30. data/lib/formular/element/bootstrap4/horizontal.rb +87 -0
  31. data/lib/formular/element/foundation6.rb +69 -0
  32. data/lib/formular/element/foundation6/checkable_control.rb +72 -0
  33. data/lib/formular/element/foundation6/input_group.rb +88 -0
  34. data/lib/formular/element/foundation6/wrapped_control.rb +21 -0
  35. data/lib/formular/element/module.rb +35 -0
  36. data/lib/formular/element/modules/checkable.rb +96 -0
  37. data/lib/formular/element/modules/collection.rb +17 -0
  38. data/lib/formular/element/modules/container.rb +60 -0
  39. data/lib/formular/element/modules/control.rb +42 -0
  40. data/lib/formular/element/modules/error.rb +48 -0
  41. data/lib/formular/element/modules/hint.rb +36 -0
  42. data/lib/formular/element/modules/label.rb +30 -0
  43. data/lib/formular/element/modules/wrapped_control.rb +73 -0
  44. data/lib/formular/elements.rb +295 -0
  45. data/lib/formular/helper.rb +53 -0
  46. data/lib/formular/html_block.rb +70 -0
  47. data/lib/formular/path.rb +30 -0
  48. data/lib/formular/version.rb +3 -0
  49. metadata +247 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d70a328fd35abcbc14db333621126deb14c40e1
4
+ data.tar.gz: a8563cdc4aa0a144b1d11d9ef960ce8337c12c52
5
+ SHA512:
6
+ metadata.gz: 11d0350fe322bfed668ce7794d5711f1cab046fe6afed225d541e191680ab7fe2035c9b3b6adc109616e95aea269bce87cd79cda79a0e862a404918a4aec1993
7
+ data.tar.gz: 3abaae426105020622039d566ef3d947613dd2f67197d212c8783a818f94bd817aa03b97e1ea70919b8c9e3455157843d00e48ccfb9292751cf37f91f52e6d81
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,29 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+ bundler_args: --without console benchmarks
5
+ script:
6
+ - bundle exec rake test
7
+ rvm:
8
+ - 2.0
9
+ - 2.1.10
10
+ - 2.2.5
11
+ - 2.3.1
12
+ - rbx-2
13
+ - jruby-9.1.1.0
14
+ - ruby-head
15
+ env:
16
+ global:
17
+ - JRUBY_OPTS='--dev -J-Xmx1024M'
18
+ matrix:
19
+ allow_failures:
20
+ - rvm: jruby-9.1.1.0
21
+ - rvm: rbx-2
22
+ - rvm: ruby-head
23
+ - rvm: jruby-head
24
+ include:
25
+ - rvm: jruby-head
26
+ before_install: gem install bundler --no-ri --no-rdoc
27
+
28
+ notifications:
29
+ email: false
@@ -0,0 +1,3 @@
1
+ # v0.2.0 2016-09-27
2
+
3
+ First public release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in formular.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Nick Sutterer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,105 @@
1
+ # Formular
2
+
3
+ _Framework-Agnostic Form Renderer for Ruby._
4
+ [![Build Status](https://travis-ci.org/trailblazer/formular.svg?branch=master)](https://travis-ci.org/trailblazer/formular)
5
+ [![Gitter Chat](https://badges.gitter.im/trailblazer/chat.svg)](https://gitter.im/trailblazer/chat)
6
+ [![TRB Newsletter](https://img.shields.io/badge/TRB-newsletter-lightgrey.svg)](http://trailblazer.to/newsletter/)
7
+
8
+
9
+ ## Overview
10
+
11
+ Formular renders HTML forms in a similar fashion to [SimpleForm](https://github.com/plataformatec/simple_form) and other gems. It is lightning-fast, has zero coupling to any ORM or web framework, and makes no magical assumptions about the rendered form object.
12
+
13
+
14
+ ## Example
15
+
16
+ While you can instantiate the form builder manually, it's easiest to use the `form` helper to do so. Include `Formular::Helper` into your cell, view, or in a Rails controller as a helper.
17
+
18
+ ```ruby
19
+ module Post::Cell
20
+ class New < Cell::ViewModel
21
+ include Formular::Helper
22
+ ```
23
+
24
+ or
25
+
26
+ ```ruby
27
+ class PostsController < ApplicationController
28
+ helper Formular::Helper
29
+ ```
30
+
31
+ You should also configure what builder you want to use. This will wrap inputs correctly, and so on.
32
+
33
+ ```ruby
34
+ Formular::Helper.builder= :bootstrap3
35
+ ```
36
+
37
+ In your view, you're now ready to use Formular's API to render forms.
38
+
39
+ ```slim
40
+ = form(model.contract, url) do |f|
41
+
42
+ = f.input :title, placeholder: "Title"
43
+ = f.input :url_slug, placeholder: "URL slug"
44
+ .form-group
45
+ = f.checkbox :is_public, label: "Public?"
46
+ .form-group
47
+ = f.radio :owner, label: "Flori", value: 1
48
+ = f.radio :owner, label: "Konsti", value: 2
49
+
50
+ .row
51
+ .col-md-2
52
+ = f.radio :owner, collection: [["Flori", 1], ["Konsti", 2]], label: "Owners"
53
+ .col-md-3
54
+ = f.radio :owner, collection: [["Flori", 1], ["Konsti", 2]], label: "Owners, inline", inline: true
55
+ = f.checkbox :roles, collection: [["Admin", 1], ["Owner", 2], ["Maintainer", 3]], checked: model.contract.roles, label: "Roles"
56
+
57
+ = f.select :select_roles, collection: [["Admin", 1], ["Owner", 2], ["Maintainer", 3]], selected: model.contract.select_roles, label: "Selectable Roles"
58
+
59
+ .form-group
60
+ = f.textarea :content, placeholder: "And your story...", rows: 9
61
+ .form-group
62
+ = f.button type: :submit, value: "Submit!", class: [:btn, :'btn-lg', :'btn-default']
63
+ ```
64
+
65
+ Note that a lot of this code can be done automatically by Formular.
66
+
67
+ ## Documentation
68
+
69
+ Formular's API docs and information on how to extend it can be found on the [Trailblazer project page](http://trailblazer.to/gems/formular).
70
+
71
+ Formular's rendering is easily customizable. It provides built-in support for Foundation 6, Bootstrap 3 and Bootstrap 4 so far with more to come.
72
+
73
+ The list of builders provided by default can be seen in the BUILDERS constant. Try the following in an IRB session with your project loaded (rails console).
74
+
75
+ ```ruby
76
+ Formular::Helper::BUILDERS
77
+ ```
78
+
79
+
80
+
81
+ ## Key Features
82
+
83
+ * Incredibly fast.
84
+ * Customization: "Wrappers" are self-explaining objects. Ships with renderers for Foundation 5 and Bootstrap 3
85
+ * No magic. No `respond_to?`, no guessing, no hidden semantics.
86
+ * A well-designed API instead of a configuration DSL. If you need to change behavior, program it.
87
+
88
+ ## Limitations
89
+
90
+ * Currently, nested hashes aren't suffixed with `_attributes`, as it's usually done in ActiveRecord.
91
+ * Capturing only works with Slim and Hamlit. A 'blockless' API is provided to enable use in ERB
92
+
93
+ ## Installation
94
+
95
+ Add this line to your application's Gemfile:
96
+
97
+ ```ruby
98
+ gem 'formular'
99
+ ```
100
+
101
+ Requires Ruby >= 2.1.
102
+
103
+ ## License
104
+
105
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ t.verbose = true
9
+ t.warning = false
10
+ end
11
+
12
+ task :default => :test
@@ -0,0 +1,33 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'formular/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "formular"
7
+ spec.version = Formular::VERSION
8
+ spec.authors = ["Nick Sutterer", "Fran Worley"]
9
+ spec.email = ["apotonick@gmail.com", "frances@safetytoolbox.co.uk"]
10
+
11
+ spec.summary = %q{Form builder based on Cells. Fast, Furious, and Framework-Agnostic.}
12
+ spec.description = %q{Customizable, fast form builder based on Cells. Framework-agnostic.}
13
+ spec.homepage = "http://trailblazer.to/gems/formular.html"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "declarative", '~> 0.0.4'
22
+ spec.add_dependency "uber", "~> 0.0.11"
23
+
24
+ spec.add_development_dependency "bundler"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "reform"
27
+ spec.add_development_dependency "dry-validation"
28
+ spec.add_development_dependency "trailblazer-cells"
29
+ spec.add_development_dependency "cells-slim"
30
+ spec.add_development_dependency "cells-erb"
31
+ spec.add_development_dependency "minitest"
32
+ spec.add_development_dependency "minitest-line"
33
+ end
@@ -0,0 +1,8 @@
1
+ require 'formular/version'
2
+
3
+ module Formular
4
+ # Your code goes here...
5
+ end
6
+
7
+ require 'formular/helper'
8
+ require 'formular/builder'
@@ -0,0 +1,45 @@
1
+ module Formular
2
+ class Attributes < Hash
3
+ def self.[](hash)
4
+ hash ||= {}
5
+ super
6
+ end
7
+
8
+ def merge(hash)
9
+ dup.merge!(hash)
10
+ end
11
+
12
+ def merge!(hash)
13
+ classes = self[:class]
14
+ new_classes = hash[:class]
15
+ return super unless classes && new_classes
16
+
17
+ hash[:class] += classes
18
+ super
19
+ end
20
+
21
+ # converts the hash into a string k1=v1 k2=v2
22
+ # replaces underscores with - so we can use regular keys
23
+ # allows one layer of nestedhashes so we can define data options as a hash.
24
+ def to_html
25
+ map do |key,val|
26
+ if val.is_a?(Hash)
27
+ val.map do |k,v|
28
+ %(#{key_to_attr_name(key)}-#{key_to_attr_name(k)}="#{val_to_string(v)}")
29
+ end.join(" ")
30
+ else
31
+ %(#{key_to_attr_name(key)}="#{val_to_string(val)}")
32
+ end
33
+ end.join(" ")
34
+ end
35
+
36
+ private
37
+ def key_to_attr_name(key)
38
+ key.to_s.gsub('_', '-')
39
+ end
40
+
41
+ def val_to_string(value)
42
+ value.is_a?(Array) ? value.join(' ') : value
43
+ end
44
+ end # class Attributes
45
+ end # module Formular
@@ -0,0 +1,52 @@
1
+ require 'formular/path'
2
+ require 'uber/inheritable_attr'
3
+ module Formular
4
+ class Builder
5
+ extend Uber::InheritableAttr
6
+ inheritable_attr :elements
7
+ self.elements = {}
8
+
9
+
10
+ def self.element_set(**elements)
11
+ self.elements.merge!(elements)
12
+ define_element_methods(self.elements)
13
+ end
14
+
15
+ def self.define_element_methods(**elements)
16
+ elements.each { |k, v| define_element_method(k, v) }
17
+ end
18
+
19
+ def self.define_element_method(element_name, element_class)
20
+ define_method(element_name) do |*args, &block|
21
+ if args.size > 1
22
+ name, options = args
23
+ else
24
+ case args.first
25
+ when Symbol then name = args.first
26
+ when Hash then options = args.first
27
+ end
28
+ end
29
+
30
+ options ||= {}
31
+ options[:builder] = self
32
+ options[:attribute_name] = name if name
33
+
34
+ element_class.(options, &block)
35
+ end
36
+ end
37
+
38
+ def initialize(**elements)
39
+ @elements = self.class.elements.merge(elements)
40
+ self.class.define_element_methods(elements) if elements
41
+ end
42
+ attr_reader :elements
43
+
44
+ def capture(*args)
45
+ yield(*args)
46
+ end
47
+
48
+ def call(&block)
49
+ capture(self, &block)
50
+ end
51
+ end # class Builder
52
+ end # module Formular
@@ -0,0 +1,64 @@
1
+ require 'formular/builder'
2
+ require 'formular/elements'
3
+ module Formular
4
+ module Builders
5
+ # I'm not quite sure why I made this a seperate class
6
+ # But I kind of see myself having Builder as a generic
7
+ # viewbuilder and this basic class as Form
8
+ class Basic < Formular::Builder
9
+ element_set(
10
+ error_notification: Formular::Element::ErrorNotification,
11
+ form: Formular::Element::Form,
12
+ fieldset: Formular::Element::Fieldset,
13
+ legend: Formular::Element::Legend,
14
+ div: Formular::Element::Div,
15
+ span: Formular::Element::Span,
16
+ p: Formular::Element::P,
17
+ input: Formular::Element::Input,
18
+ hidden: Formular::Element::Hidden,
19
+ label: Formular::Element::Label,
20
+ error: Formular::Element::Error,
21
+ hint: Formular::Element::P,
22
+ textarea: Formular::Element::Textarea,
23
+ submit: Formular::Element::Submit,
24
+ select: Formular::Element::Select,
25
+ checkbox: Formular::Element::Checkbox,
26
+ radio: Formular::Element::Radio,
27
+ wrapper: Formular::Element::Div,
28
+ error_wrapper: Formular::Element::Div
29
+ )
30
+
31
+ def initialize(model: nil, path_prefix: nil, errors: nil, elements: {})
32
+ @model = model
33
+ @path_prefix = path_prefix
34
+ @errors = errors || (model ? model.errors : {})
35
+ super(elements)
36
+ end
37
+ attr_reader :model, :errors
38
+
39
+ def collection(name, models: nil, builder: nil, &block)
40
+ models ||= model ? model.send(name) : []
41
+
42
+ models.map.with_index do |model, i|
43
+ nested(name, nested_model: model, path_appendix: [name,i], builder: builder, &block)
44
+ end.join('')
45
+ end
46
+
47
+ def nested(name, nested_model: nil, path_appendix: nil, builder: nil, &block)
48
+ nested_model ||= model.send(name) if model
49
+ path_appendix ||= name
50
+ builder ||= self.class
51
+ builder.new(model: nested_model, path_prefix: path(path_appendix)).(&block)
52
+ end
53
+
54
+ # these can be called from an element
55
+ def path(appendix = nil)
56
+ appendix ? Path[*@path_prefix, appendix] : Path[@path_prefix]
57
+ end
58
+
59
+ def reader_value(name)
60
+ model ? model.send(name) : nil
61
+ end
62
+ end # class Basic
63
+ end # module Builders
64
+ end # module Formular
@@ -0,0 +1,28 @@
1
+ require 'formular/builders/basic'
2
+ require 'formular/element/bootstrap3'
3
+ require 'formular/element/bootstrap3/input_group'
4
+ module Formular
5
+ module Builders
6
+ class Bootstrap3 < Formular::Builders::Basic
7
+ element_set(
8
+ error_notification: Formular::Element::Bootstrap3::ErrorNotification,
9
+ error: Formular::Element::Bootstrap3::Error,
10
+ hint: Formular::Element::Bootstrap3::Hint,
11
+ input: Formular::Element::Bootstrap3::Input,
12
+ input_group: Formular::Element::Bootstrap3::InputGroup,
13
+ checkbox: Formular::Element::Bootstrap3::Checkbox,
14
+ radio: Formular::Element::Bootstrap3::Radio,
15
+ select: Formular::Element::Bootstrap3::Select,
16
+ inline_radio: Formular::Element::Bootstrap3::InlineRadio,
17
+ inline_checkbox: Formular::Element::Bootstrap3::InlineCheckbox,
18
+ label: Formular::Element::Bootstrap3::Label,
19
+ checkable_group_label: Formular::Element::Bootstrap3::Label,
20
+ textarea: Formular::Element::Bootstrap3::Textarea,
21
+ wrapper: Formular::Element::Bootstrap3::Wrapper,
22
+ error_wrapper: Formular::Element::Bootstrap3::ErrorWrapper,
23
+ submit: Formular::Element::Bootstrap3::Submit,
24
+ row: Formular::Element::Bootstrap3::Row
25
+ )
26
+ end # class Bootstrap3
27
+ end # module Builders
28
+ end # module Formular
@@ -0,0 +1,32 @@
1
+ require 'formular/builders/bootstrap3'
2
+ require 'formular/element/bootstrap3/horizontal'
3
+ module Formular
4
+ module Builders
5
+ class Bootstrap3Horizontal < Formular::Builders::Bootstrap3
6
+ element_set(
7
+ form: Formular::Element::Bootstrap3::Horizontal::Form,
8
+ input: Formular::Element::Bootstrap3::Horizontal::Input,
9
+ input_group: Formular::Element::Bootstrap3::Horizontal::InputGroup,
10
+ select: Formular::Element::Bootstrap3::Horizontal::Select,
11
+ checkbox: Formular::Element::Bootstrap3::Horizontal::Checkbox,
12
+ radio: Formular::Element::Bootstrap3::Horizontal::Radio,
13
+ inline_radio: Formular::Element::Bootstrap3::Horizontal::InlineRadio,
14
+ inline_checkbox: Formular::Element::Bootstrap3::Horizontal::InlineCheckbox,
15
+ label: Formular::Element::Bootstrap3::Horizontal::Label,
16
+ checkable_group_label: Formular::Element::Bootstrap3::Horizontal::Label,
17
+ textarea: Formular::Element::Bootstrap3::Horizontal::Textarea,
18
+ error_wrapper: Formular::Element::Bootstrap3::ErrorWrapper,
19
+ input_column_wrapper: Formular::Element::Bootstrap3::Horizontal::InputColumnWrapper,
20
+ submit: Formular::Element::Bootstrap3::Horizontal::Submit
21
+ )
22
+ inheritable_attr :column_classes
23
+
24
+ #these options should be easily configurable
25
+ self.column_classes = {
26
+ left_column: ['col-sm-2'],
27
+ right_column: ['col-sm-10'],
28
+ left_offset: ['col-sm-offset-2']
29
+ }
30
+ end # class Bootstrap3Horizontal
31
+ end # module Builders
32
+ end # module Formular