formular 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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