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