Skip to content
This repository has been archived by the owner on Oct 17, 2023. It is now read-only.

Re-implement as Rails action view helper, remove plain ruby compatibility #5

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ CHANGELOG
- [View Diff](https://github.com/westonganger/sexy_form.rb/compare/v0.9.0...master)
- [#2](https://github.com/westonganger/sexy_form.rb/pulls/2) - Remove validations on field `:type` option
- Fix bug when field `:errors` is provided but is empty
- Reimplement as a Rails actionview plugin. No easy way to do something similar to Rails `capture` that is framework agnostic. This gem was essentially broken before this.

- **0.9.0** - February 15, 2019
- Gem Initial Release
28 changes: 8 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Dead simple HTML form builder for Ruby with built-in support for many popular UI

Out of the box Form Builder can generate HTML markup for the following UI libraries:

- Bootstrap 4
- Bootstrap 4
* `theme: :bootstrap_4_vertical`
* `theme: :bootstrap_4_inline`
* `theme: :bootstrap_4_horizontal` or `theme: SexyForm::Themes::Bootstrap4Horizontal.new(column_classes: ["col-sm-3","col-sm-9"])`
Expand Down Expand Up @@ -71,7 +71,7 @@ The following field types are supported:
## SexyForm in View Templates (Example in Slim)

```ruby
= SexyForm.form(theme: :bootstrap_4_vertical, action: "/products", method: :post, form_html: {style: "margin-top: 20px;", "data-foo" => "bar"}) do |f|
= sexy_form(theme: :bootstrap_4_vertical, action: "/products", method: :post, form_html: {style: "margin-top: 20px;", "data-foo" => "bar"}) do |f|
.row.main-examples
.col-sm-6
### -- Field Options
Expand All @@ -91,8 +91,8 @@ The following field types are supported:
### label_html : (Optional) Hash ### contains attributes to be added to the label
### wrapper_html : (Optional) Hash ### contains attributes to be added to the outer wrapper for the label and input
### help_text_html : (Optional) Hash ### contains attributes to be added to the help text container
### error_html : (Optional) Hash ### contains attributes to be added to the error container(s)
### error_html : (Optional) Hash ### contains attributes to be added to the error container(s)

= f.field name: "product[name]", label: "Name", type: :text, errors: product_errors["name"]

= f.field name: "product[description]", label: "Description", type: :textarea, input_html: {class: "foobar"}, wrapper_html: {style: "margin-top: 10px"}, label_html: {style: "color: red;"}
Expand Down Expand Up @@ -122,18 +122,6 @@ The following field types are supported:
= f.field name: "product[type]", label: "Type", type: :select, collection: {options: opts, selected: ["B"], disabled: ["C"]}
```

## SexyForm in Plain Ruby Code

When using the `SexyForm.form` method in plain Ruby code, the `<<` syntax is required to add the generated field HTML to the form HTML string

```ruby
form_html_str = SexyForm.form(theme: :bootstrap_4_vertical, action: "/products", method: :post, form_html: {style: "margin-top: 20px;", "data-foo" => "bar"}) do |f|
f << f.field(name: "name", type: :text, label: "Name")
f << f.field(name: "sku", type: :text, label: "SKU")
f << %Q(<strong>Hello World</strong>"
end
```

## SexyForm without a Form

```ruby
Expand Down Expand Up @@ -200,9 +188,9 @@ module SexyForm
s << "#{html_label}"
s << "#{html_field}"
end

s << "#{html_help_text}"

if html_errors
s << html_errors.join
end
Expand All @@ -215,11 +203,11 @@ module SexyForm
def input_html_attributes(field_type: , has_errors: , html_attrs:)
html_attrs["class"] = "form-field other-class #{html_attrs["class"]}".strip
html_attrs["style"] = "color: blue; #{html_attrs["style"]}".strip

unless html_attrs.has_key?("data-foo")
html_attrs["data-foo"] = "bar"
end

html_attrs
end

Expand Down
14 changes: 7 additions & 7 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
require File.expand_path(File.dirname(__FILE__) + '/lib/sexy_form/version')
require 'bundler/gem_tasks'

task :console do
require 'sexy_form'

require 'irb'
binding.irb
end

require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)

task default: :spec
task test: :spec

task :console do
require_relative 'test/test_helper'

require 'irb'
binding.irb
end
48 changes: 7 additions & 41 deletions lib/sexy_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,18 @@
end

require "sexy_form/builder"
require "sexy_form/action_view_helpers"

module SexyForm
def self.form(action: nil, method: "post", theme: nil, form_html: {})
self.verify_argument_type(arg_name: :form_html, value: form_html, expected_type: Hash)

action = action.to_s
method = method.to_s

builder = SexyForm::Builder.new(theme: theme)

themed_form_html = builder.theme.form_html_attributes(html_attrs: self.safe_string_hash(form_html))

themed_form_html["method"] = method.to_s == "get" ? "get" : "post"

if themed_form_html["multipart"] == true
themed_form_html.delete("multipart")
themed_form_html["enctype"] = "multipart/form-data"
end

str = ""

str << %Q(<form #{self.build_html_attr_string(themed_form_html)}>)

unless ["get", "post"].include?(method.to_s)
str << %Q(<input type="hidden" name="_method" value="#{method}")
end

if block_given?
yield builder
end

unless builder.html_string.empty?
str << builder.html_string
end

str << "</form>"

str
end

protected

def self.build_html_attr_string(hash)
hash.map{|k, v| "#{k}=\"#{v}\""}.join(" ")
hash.delete_if{|_,v| v.nil? || v.to_s.strip.empty? }.map{|k, v| "#{k}=\"#{v.to_s.strip}\""}.join(" ")
end

def self.build_html_element(type, hash)
attr_str = build_html_attr_string(hash)
attr_str.empty? ? "<#{type}>" : "<#{type} #{attr_str}>"
end

def self.safe_string_hash(h)
Expand Down Expand Up @@ -79,6 +47,4 @@ def self.verify_argument_type(arg_name:, value:, expected_type:)
raise ArgumentError.new("Invalid type passed to argument :#{arg_name}")
end
end
### END PROTECTED METHODS

end
43 changes: 43 additions & 0 deletions lib/sexy_form/action_view_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module SexyForm
module ActionViewHelpers
def sexy_form(action: nil, method: "post", theme: nil, form_html: {}, &block)
SexyForm.verify_argument_type(arg_name: :form_html, value: form_html, expected_type: Hash)

action = action.to_s
method = method.to_s

builder = SexyForm::Builder.new(theme: theme)

themed_form_html = builder.theme.form_html_attributes(html_attrs: self.safe_string_hash(form_html))

themed_form_html["method"] = (method.to_s == "get" ? "get" : "post")

if themed_form_html["multipart"] == true
themed_form_html.delete("multipart")
themed_form_html["enctype"] = "multipart/form-data"
end

str = ""

str << SexyForm.build_html_element(:form, themed_form_html)

unless ["get", "post"].include?(method.to_s)
str << %Q(<input type="hidden" name="_method" value="#{method}")
end

if block_given?
str << capture(builder, &block)
end

str << "</form>"

str.html_safe
end
end
end

require "active_support/lazy_load_hooks"

ActiveSupport.on_load(:action_view) do
include SexyForm::ActionViewHelpers
end
13 changes: 0 additions & 13 deletions lib/sexy_form/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ class Builder
COLLECTION_KEYS = ["options", "selected", "disabled", "include_blank"].freeze

def initialize(theme: nil)
@html = []

if theme
if theme.is_a?(SexyForm::Themes::BaseTheme)
@theme = theme
Expand All @@ -21,17 +19,6 @@ def theme
@theme
end

def <<(v)
v = v.to_s
@html.push(v)
v
end

### This method should be considered private
def html_string
@html.join("")
end

def field(
type: ,
name: nil,
Expand Down
8 changes: 5 additions & 3 deletions sexy_form.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ Gem::Specification.new do |s|
s.author = "Weston Ganger"
s.email = '[email protected]'
s.homepage = 'https://github.com/westonganger/sexy_form'

s.summary = "Dead simple HTML form builder for Ruby with built-in support for many popular UI libraries such as Bootstrap"

s.description = s.summary
s.description = s.summary

s.files = Dir.glob("{lib/**/*}") + %w{ LICENSE README.md Rakefile CHANGELOG.md }
s.require_path = 'lib'

s.required_ruby_version = '>= 1.9.3'


s.add_dependency 'actionview'

s.add_development_dependency 'rake'
s.add_development_dependency 'bundler'
s.add_development_dependency 'rspec'
Expand Down
7 changes: 7 additions & 0 deletions spec/dummy/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require File.expand_path('../config/application', __FILE__)

Dummy::Application.load_tasks
3 changes: 3 additions & 0 deletions spec/dummy/app/assets/config/manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//= link_tree ../images
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css .scss
Empty file.
3 changes: 3 additions & 0 deletions spec/dummy/app/assets/stylesheets/application.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/*
*= require_self
*/
3 changes: 3 additions & 0 deletions spec/dummy/app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ApplicationController < ActionController::Base
protect_from_forgery
end
Empty file added spec/dummy/app/mailers/.gitkeep
Empty file.
3 changes: 3 additions & 0 deletions spec/dummy/app/models/application_record.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
5 changes: 5 additions & 0 deletions spec/dummy/app/models/comment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Comment < ActiveRecord::Base
include ActiveSnapshot

belongs_to :post
end
Empty file.
5 changes: 5 additions & 0 deletions spec/dummy/app/models/note.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Note < ActiveRecord::Base
include ActiveSnapshot

belongs_to :post
end
5 changes: 5 additions & 0 deletions spec/dummy/app/models/parent_without_children.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class ParentWithoutChildren < ApplicationRecord
include ActiveSnapshot

self.table_name = "posts"
end
16 changes: 16 additions & 0 deletions spec/dummy/app/models/post.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Post < ActiveRecord::Base
include ActiveSnapshot

has_many :comments
has_many :notes

has_snapshot_children do
instance = self.class.includes(:comments, :notes).find(id)

{
comments: instance.comments,
notes: instance.notes,
nil_assoc: nil,
}
end
end
11 changes: 11 additions & 0 deletions spec/dummy/app/models/sub_post.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class SubPost < Post
has_snapshot_children do
instance = self.class.includes(:comments, :notes).find(id)

{
comments: instance.comments,
notes: instance.notes,
nil_assoc: nil,
}
end
end
5 changes: 5 additions & 0 deletions spec/dummy/app/models/volatile_post.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class VolatilePost < ActiveRecord::Base
self.table_name = "posts"

include ActiveSnapshot
end
14 changes: 14 additions & 0 deletions spec/dummy/app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>Dummy App</title>
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>
4 changes: 4 additions & 0 deletions spec/dummy/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# This file is used by Rack-based servers to start the application.

require ::File.expand_path('../config/environment', __FILE__)
run Dummy::Application
Loading