express_templates 0.3.6 → 0.4.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.
- checksums.yaml +4 -4
- data/lib/core_extensions/proc.rb +1 -5
- data/lib/express_templates/compiler.rb +10 -9
- data/lib/express_templates/components.rb +1 -1
- data/lib/express_templates/components/capabilities/adoptable.rb +20 -0
- data/lib/express_templates/components/capabilities/building.rb +6 -0
- data/lib/express_templates/components/capabilities/parenting.rb +7 -2
- data/lib/express_templates/components/capabilities/templating.rb +1 -1
- data/lib/express_templates/components/for_each.rb +2 -0
- data/lib/express_templates/components/form_rails_support.rb +4 -1
- data/lib/express_templates/components/forms.rb +15 -0
- data/lib/express_templates/components/forms/basic_fields.rb +53 -0
- data/lib/express_templates/components/forms/checkbox.rb +37 -0
- data/lib/express_templates/components/forms/express_form.rb +66 -0
- data/lib/express_templates/components/forms/form_component.rb +60 -0
- data/lib/express_templates/components/forms/option_support.rb +43 -0
- data/lib/express_templates/components/forms/radio.rb +84 -0
- data/lib/express_templates/components/forms/select.rb +61 -0
- data/lib/express_templates/components/forms/submit.rb +26 -0
- data/lib/express_templates/components/null_wrap.rb +33 -1
- data/lib/express_templates/expander.rb +1 -0
- data/lib/express_templates/version.rb +1 -1
- data/test/components/container_test.rb +2 -0
- data/test/components/forms/basic_fields_test.rb +52 -0
- data/test/components/forms/checkbox_test.rb +44 -0
- data/test/components/forms/express_form_test.rb +120 -0
- data/test/components/forms/radio_test.rb +91 -0
- data/test/components/forms/select_test.rb +75 -0
- data/test/components/forms/submit_test.rb +12 -0
- data/test/components/iterating_test.rb +18 -0
- data/test/components/null_wrap_test.rb +28 -0
- data/test/components/table_for_test.rb +6 -6
- data/test/dummy/log/test.log +13758 -0
- data/test/markup/tag_test.rb +1 -1
- metadata +26 -5
- data/lib/express_templates/components/form_for.rb +0 -469
- data/test/components/form_for_test.rb +0 -246
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ccf72f73138b37943b3f07f72fa8cff812ab095
|
4
|
+
data.tar.gz: 451eb47c997d6d6d7dd587a12278a3b8f1e8ef16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a202286780ebf286604d50fe9708ff3a578845ad399306f203a6f9cc0fee382979a5f3d9355912f2ed5b91c6609e5df2c022c27b8a50bee5c8fec8c8db387204
|
7
|
+
data.tar.gz: cdcca66c5c69acd5cf172d42bb2176d8f35ec842ffb7db95f1dd2a2bddfb94cc86a5bf966f47d71d6af656c3b334346269aba4adf6f7a046afc25c8fc35b628c
|
data/lib/core_extensions/proc.rb
CHANGED
@@ -82,11 +82,7 @@ class Proc
|
|
82
82
|
|
83
83
|
def self.from_source(prc_src)
|
84
84
|
raise ArgumentError unless prc_src.kind_of?(String)
|
85
|
-
prc =
|
86
|
-
eval(prc_src)
|
87
|
-
rescue ArgumentError => e
|
88
|
-
binding.pry
|
89
|
-
end
|
85
|
+
prc = eval(prc_src)
|
90
86
|
prc.instance_variable_set(:@source, prc_src)
|
91
87
|
prc
|
92
88
|
end
|
@@ -1,19 +1,20 @@
|
|
1
1
|
module ExpressTemplates
|
2
2
|
module Compiler
|
3
3
|
def compile(template_or_src=nil, &block)
|
4
|
+
|
5
|
+
if block
|
6
|
+
begin
|
7
|
+
block.source
|
8
|
+
rescue
|
9
|
+
raise "block must have source - did you do compile(&:label) ?"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
4
13
|
template, src = _normalize(template_or_src)
|
5
14
|
|
6
15
|
expander = Expander.new(template)
|
7
16
|
|
8
|
-
|
9
|
-
Thread.current[:first_whitepace_removed] += 1
|
10
|
-
begin
|
11
|
-
compiled = expander.expand(src, &block).map(&:compile)
|
12
|
-
compiled.first.sub!(/^"\n+/, '"') if Thread.current[:first_whitepace_removed].eql?(1)
|
13
|
-
Thread.current[:first_whitepace_removed] -= 1
|
14
|
-
ensure
|
15
|
-
Thread.current[:first_whitepace_removed] = nil if Thread.current[:first_whitepace_removed].eql?(0)
|
16
|
-
end
|
17
|
+
compiled = expander.expand(src, &block).map(&:compile)
|
17
18
|
|
18
19
|
return Interpolator.transform(compiled.join("+").gsub('"+"', '')).tap do |s|
|
19
20
|
puts("\n"+template.inspect+"\nSource:\n#{template.try(:source)}\nInterpolated:\n#{s}\n") if ENV['DEBUG'].eql?('true')
|
@@ -12,7 +12,7 @@ require 'express_templates/components/unless_block'
|
|
12
12
|
require 'express_templates/components/row'
|
13
13
|
require 'express_templates/components/column'
|
14
14
|
require 'express_templates/components/form_rails_support'
|
15
|
-
require 'express_templates/components/form_for'
|
16
15
|
require 'express_templates/components/content_for'
|
17
16
|
require 'express_templates/components/table_for'
|
18
17
|
require 'express_templates/components/tree_for'
|
18
|
+
require 'express_templates/components/forms'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Capabilities
|
4
|
+
module Adoptable
|
5
|
+
# Adoptable adds the capability for a child to refer
|
6
|
+
# to its parent. This is used by more complex
|
7
|
+
# components which are intended to work together
|
8
|
+
# such as form components where form elements may need
|
9
|
+
# to use information known only to the parent.
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.class_eval do
|
13
|
+
attr_accessor :parent
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -2,6 +2,12 @@ module ExpressTemplates
|
|
2
2
|
module Components
|
3
3
|
module Capabilities
|
4
4
|
module Building
|
5
|
+
# commented this out because it's intercepting calls to rails helpers
|
6
|
+
# TODO: fix this... I think the whole buiding approach is broken.
|
7
|
+
# this class is empty and should probably go away.
|
8
|
+
# def method_missing(name, *args)
|
9
|
+
# raise "#{self.class.to_s} has no method '#{name}'"
|
10
|
+
# end
|
5
11
|
end
|
6
12
|
end
|
7
13
|
end
|
@@ -40,11 +40,16 @@ module ExpressTemplates
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def children=(children)
|
43
|
-
@children =children
|
43
|
+
@children = children
|
44
|
+
children.each do |child|
|
45
|
+
if child.kind_of?(Adoptable)
|
46
|
+
child.parent = self
|
47
|
+
end
|
48
|
+
end
|
44
49
|
end
|
45
50
|
|
46
51
|
def compile
|
47
|
-
null_wrapped_children = "null_wrap
|
52
|
+
null_wrapped_children = "null_wrap(%q(#{compile_children}))"
|
48
53
|
wrap_children_src = self.class[:markup].source.gsub(/(\s)_yield(\s)/, '\1'+null_wrapped_children+'\2')
|
49
54
|
_compile_fragment(Proc.from_source(wrap_children_src))
|
50
55
|
end
|
@@ -118,7 +118,7 @@ module ExpressTemplates
|
|
118
118
|
# Returns a string containing ruby code which evaluates to markup.
|
119
119
|
def _lookup(name, options = {})
|
120
120
|
@fragments ||= Hash.new
|
121
|
-
@fragments[name] or
|
121
|
+
@fragments[name] or raise "no template fragment supplied for: #{name}"
|
122
122
|
end
|
123
123
|
|
124
124
|
private
|
@@ -10,7 +10,10 @@ module ExpressTemplates
|
|
10
10
|
emits -> {
|
11
11
|
div(style: 'display:none') {
|
12
12
|
utf8_enforcer_tag
|
13
|
-
|
13
|
+
# NOTE: This should be moved into the forms module and made a FormComponent
|
14
|
+
# to have access to the resource_name as this code assumes existence of
|
15
|
+
# a resource method which may not exist
|
16
|
+
method_tag(@config[:id] || "{{((resource.persisted? ? :put : :post) rescue :post)}}")
|
14
17
|
token_tag
|
15
18
|
}
|
16
19
|
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Forms
|
4
|
+
end
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'express_templates/components/forms/express_form'
|
9
|
+
require 'express_templates/components/forms/form_component'
|
10
|
+
require 'express_templates/components/forms/option_support'
|
11
|
+
require 'express_templates/components/forms/submit'
|
12
|
+
require 'express_templates/components/forms/select'
|
13
|
+
require 'express_templates/components/forms/radio'
|
14
|
+
require 'express_templates/components/forms/checkbox'
|
15
|
+
require 'express_templates/components/forms/basic_fields'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Forms
|
4
|
+
module BasicFields
|
5
|
+
ALL = %w(email phone text password color date datetime
|
6
|
+
datetime_local number range
|
7
|
+
search telephone time url week)
|
8
|
+
|
9
|
+
ALL.each do |type|
|
10
|
+
class_definition = <<-RUBY
|
11
|
+
class #{type.classify} < FormComponent
|
12
|
+
emits -> {
|
13
|
+
div(class: field_wrapper_class) {
|
14
|
+
label_tag(label_name, label_text)
|
15
|
+
#{type}_field resource_var, field_name.to_sym
|
16
|
+
}
|
17
|
+
}
|
18
|
+
end
|
19
|
+
RUBY
|
20
|
+
|
21
|
+
eval(class_definition)
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# class Email < FormComponent
|
27
|
+
# emits -> {
|
28
|
+
# div(class: field_wrapper_class) {
|
29
|
+
# email_field resource_var, field_name.to_sym
|
30
|
+
# }
|
31
|
+
# }
|
32
|
+
# end
|
33
|
+
|
34
|
+
class Textarea < FormComponent
|
35
|
+
emits -> {
|
36
|
+
div(class: field_wrapper_class) {
|
37
|
+
label_tag(label_name, label_text)
|
38
|
+
text_area resource_var, field_name.to_sym
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
class Hidden < FormComponent
|
44
|
+
emits -> {
|
45
|
+
hidden_field resource_var, field_name.to_sym
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Forms
|
4
|
+
class Checkbox < FormComponent
|
5
|
+
|
6
|
+
emits -> {
|
7
|
+
div(class: field_wrapper_class) {
|
8
|
+
label_tag(label_name, label_text) if label_before?
|
9
|
+
check_box(resource_var, field_name.to_sym, field_options, checked_value, unchecked_value)
|
10
|
+
label_tag(label_name, label_text) if label_after?
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
def label_before?
|
15
|
+
!label_after?
|
16
|
+
end
|
17
|
+
|
18
|
+
def label_after?
|
19
|
+
!!@config[:label_after]
|
20
|
+
end
|
21
|
+
|
22
|
+
def field_options
|
23
|
+
{}
|
24
|
+
end
|
25
|
+
|
26
|
+
def checked_value
|
27
|
+
"1"
|
28
|
+
end
|
29
|
+
|
30
|
+
def unchecked_value
|
31
|
+
"0"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Forms
|
4
|
+
class ExpressForm < Base
|
5
|
+
include Capabilities::Configurable
|
6
|
+
include Capabilities::Parenting
|
7
|
+
|
8
|
+
emits -> {
|
9
|
+
form( form_args ) {
|
10
|
+
form_rails_support form_method
|
11
|
+
_yield
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
def form_id
|
16
|
+
"#{resource_name}_{{@#{resource_name}.id}}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def form_method
|
20
|
+
@config[:method]
|
21
|
+
end
|
22
|
+
|
23
|
+
def form_action
|
24
|
+
if _modifying_resource?
|
25
|
+
"{{#{resource_name_for_path}_path(@#{resource_name})}}"
|
26
|
+
else # posting a new to a collection
|
27
|
+
# We also have to take in to account singular resources
|
28
|
+
# e.g. resource :config -> will throw an unknown method error of configs_path
|
29
|
+
"{{#{resource_name_for_path.pluralize}_path}}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def form_args
|
35
|
+
# there are no put/patch emthods in HTML5, so we have to enforce post
|
36
|
+
# need to find a better way to do this: id/action can be overridden but method
|
37
|
+
# should always be :post IN THE FORM TAG
|
38
|
+
args = {id: form_id, action: form_action}.merge!(@config).merge!(method: :post)
|
39
|
+
|
40
|
+
if html_options = args.delete(:html_options)
|
41
|
+
args.merge!(html_options)
|
42
|
+
end
|
43
|
+
args[:method] = args[:method].to_s.upcase
|
44
|
+
args
|
45
|
+
end
|
46
|
+
|
47
|
+
def resource_name_for_path
|
48
|
+
@config[:id].to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
def resource_name
|
52
|
+
(@config[:resource_name] || @config[:id]).to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def _modifying_resource?
|
59
|
+
[:put, :patch].include? form_method
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Forms
|
4
|
+
class FormComponent < Base
|
5
|
+
include Capabilities::Configurable
|
6
|
+
include Capabilities::Adoptable
|
7
|
+
|
8
|
+
def compile(*args)
|
9
|
+
raise "#{self.class} requires a parent ExpressForm" if parent.nil? or parent_form.nil?
|
10
|
+
super(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Lookup the resource_name from the parent ExpressForm.
|
14
|
+
def resource_name
|
15
|
+
parent_form.resource_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def resource_var
|
19
|
+
resource_name.to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return the name attribute for the lable
|
23
|
+
def label_name
|
24
|
+
"#{resource_name.singularize}_#{field_name}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return the text content for the label
|
28
|
+
def label_text
|
29
|
+
@options[:label] || field_name.titleize
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return the field_name as a string. This taken from the first argument
|
33
|
+
# to the component macro in the template or fragment.
|
34
|
+
def field_name
|
35
|
+
(@config[:id] || (@args.first.is_a?(String) && @args.first)).to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return the field name attribute. Currently handles only simple attributes
|
39
|
+
# on the resource. Does not handle attributes for associated resources.
|
40
|
+
def field_name_attribute
|
41
|
+
"#{resource_name.singularize}[#{field_name}]"
|
42
|
+
end
|
43
|
+
|
44
|
+
def field_wrapper_class
|
45
|
+
@config[:wrapper_class] || 'field-wrapper'
|
46
|
+
end
|
47
|
+
|
48
|
+
# Search the parent graph until we find an ExpressForm. Returns nil if none found.
|
49
|
+
def parent_form
|
50
|
+
@my_form ||= parent
|
51
|
+
until @my_form.nil? || @my_form.kind_of?(ExpressForm)
|
52
|
+
@my_form = @my_form.parent
|
53
|
+
end
|
54
|
+
return @my_form
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Forms
|
4
|
+
# Provides a form component with knowledge of any association
|
5
|
+
# on the field and an means of loading the collection for supplying
|
6
|
+
# options to the user.
|
7
|
+
module OptionSupport
|
8
|
+
# Reflect on any association and return it if the association type
|
9
|
+
# is :belongs_to. Returns false if the association is not :belongs_to.
|
10
|
+
# Returns nil if there was a problem reflecting.
|
11
|
+
def belongs_to_association
|
12
|
+
begin
|
13
|
+
reflection = resource_name.classify.constantize.reflect_on_association(field_name.to_sym)
|
14
|
+
if reflection.macro.eql?(:belongs_to)
|
15
|
+
return reflection
|
16
|
+
end
|
17
|
+
rescue
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Provide ActiveRecord code to load the associated collection as
|
23
|
+
# options for display.
|
24
|
+
def related_collection
|
25
|
+
if reflection = belongs_to_association
|
26
|
+
"#{reflection.klass}.all.select(:#{option_value_method}, :#{option_name_method}).order(:name)"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def option_value_method
|
33
|
+
:id
|
34
|
+
end
|
35
|
+
|
36
|
+
def option_name_method
|
37
|
+
:name
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module ExpressTemplates
|
2
|
+
module Components
|
3
|
+
module Forms
|
4
|
+
class Radio < FormComponent
|
5
|
+
include OptionSupport
|
6
|
+
|
7
|
+
emits -> {
|
8
|
+
div(class: field_wrapper_class) {
|
9
|
+
label_tag(label_name, label_text)
|
10
|
+
if option_values_specified?
|
11
|
+
generate_options_from_specified_values
|
12
|
+
else
|
13
|
+
use_options_from_collection_radio_buttons_helper
|
14
|
+
end
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
def use_options_from_collection_radio_buttons_helper
|
19
|
+
# Note {{ }} will get stripped. This prevents the collection finder string being passed as string.
|
20
|
+
collection_radio_buttons(resource_var, field_name.to_sym, "{{#{collection_from_association}}}",
|
21
|
+
option_value_method, option_name_method,
|
22
|
+
field_options, html_options) do |b|
|
23
|
+
b.label(class: "radio") {
|
24
|
+
b.radio_button + b.text
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def option_values_specified?
|
30
|
+
[Array, Hash].include?(option_collection.class)
|
31
|
+
end
|
32
|
+
|
33
|
+
def option_collection
|
34
|
+
@args.second
|
35
|
+
end
|
36
|
+
|
37
|
+
def wrapper_class
|
38
|
+
@config[:wrapper_class] || 'wrapper-class'
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate_options_from_specified_values
|
42
|
+
case
|
43
|
+
when option_collection.kind_of?(Array)
|
44
|
+
option_collection.each_with_index do |option, index|
|
45
|
+
label(class: wrapper_class) {
|
46
|
+
radio_button(resource_var, field_name.to_sym, option, class: 'radio')
|
47
|
+
null_wrap { option }
|
48
|
+
}
|
49
|
+
end
|
50
|
+
when option_collection.kind_of?(Hash)
|
51
|
+
option_collection.each_pair do |key, value|
|
52
|
+
label(class: wrapper_class) {
|
53
|
+
radio_button(resource_var, field_name.to_sym, key, class: 'radio')
|
54
|
+
null_wrap { value }
|
55
|
+
}
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise "Radio collection should be Array or Hash: #{option_collection.inspect}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def collection_from_association
|
63
|
+
related_collection or raise "No association collection for: #{resource_name}.#{field_name}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def field_options
|
67
|
+
# If field_otions is omitted the Expander will be
|
68
|
+
# in last or 3rd position and we don't want that
|
69
|
+
if @args.size > 3 && @args[2].is_a?(Hash)
|
70
|
+
@args[2]
|
71
|
+
else
|
72
|
+
{}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# TODO: implement
|
77
|
+
def html_options
|
78
|
+
{}
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|