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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/core_extensions/proc.rb +1 -5
  3. data/lib/express_templates/compiler.rb +10 -9
  4. data/lib/express_templates/components.rb +1 -1
  5. data/lib/express_templates/components/capabilities/adoptable.rb +20 -0
  6. data/lib/express_templates/components/capabilities/building.rb +6 -0
  7. data/lib/express_templates/components/capabilities/parenting.rb +7 -2
  8. data/lib/express_templates/components/capabilities/templating.rb +1 -1
  9. data/lib/express_templates/components/for_each.rb +2 -0
  10. data/lib/express_templates/components/form_rails_support.rb +4 -1
  11. data/lib/express_templates/components/forms.rb +15 -0
  12. data/lib/express_templates/components/forms/basic_fields.rb +53 -0
  13. data/lib/express_templates/components/forms/checkbox.rb +37 -0
  14. data/lib/express_templates/components/forms/express_form.rb +66 -0
  15. data/lib/express_templates/components/forms/form_component.rb +60 -0
  16. data/lib/express_templates/components/forms/option_support.rb +43 -0
  17. data/lib/express_templates/components/forms/radio.rb +84 -0
  18. data/lib/express_templates/components/forms/select.rb +61 -0
  19. data/lib/express_templates/components/forms/submit.rb +26 -0
  20. data/lib/express_templates/components/null_wrap.rb +33 -1
  21. data/lib/express_templates/expander.rb +1 -0
  22. data/lib/express_templates/version.rb +1 -1
  23. data/test/components/container_test.rb +2 -0
  24. data/test/components/forms/basic_fields_test.rb +52 -0
  25. data/test/components/forms/checkbox_test.rb +44 -0
  26. data/test/components/forms/express_form_test.rb +120 -0
  27. data/test/components/forms/radio_test.rb +91 -0
  28. data/test/components/forms/select_test.rb +75 -0
  29. data/test/components/forms/submit_test.rb +12 -0
  30. data/test/components/iterating_test.rb +18 -0
  31. data/test/components/null_wrap_test.rb +28 -0
  32. data/test/components/table_for_test.rb +6 -6
  33. data/test/dummy/log/test.log +13758 -0
  34. data/test/markup/tag_test.rb +1 -1
  35. metadata +26 -5
  36. data/lib/express_templates/components/form_for.rb +0 -469
  37. data/test/components/form_for_test.rb +0 -246
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e11659af9b175019fa94c33686d4f4f197fcc07
4
- data.tar.gz: 3bd1729777599d3ba3772c72d8b403098aa66b07
3
+ metadata.gz: 0ccf72f73138b37943b3f07f72fa8cff812ab095
4
+ data.tar.gz: 451eb47c997d6d6d7dd587a12278a3b8f1e8ef16
5
5
  SHA512:
6
- metadata.gz: 34b7fedf65a87724dadb3d8c77ecdc78dbef4598d0bf2da4cecbc89bfbbba865bdbf9a9fbf3518d197f93597c0091a18153df7919a988f31bf786b3ef1b257dd
7
- data.tar.gz: b55b2a3cb61868d5df45738d8c1cfbf423a0bbc60996aca66c65d7cfc72d75dad0bd4831ae352c24c372dbd08fbba6e3fc775c1a5aba08eb295a0960096d0c14
6
+ metadata.gz: a202286780ebf286604d50fe9708ff3a578845ad399306f203a6f9cc0fee382979a5f3d9355912f2ed5b91c6609e5df2c022c27b8a50bee5c8fec8c8db387204
7
+ data.tar.gz: cdcca66c5c69acd5cf172d42bb2176d8f35ec842ffb7db95f1dd2a2bddfb94cc86a5bf966f47d71d6af656c3b334346269aba4adf6f7a046afc25c8fc35b628c
@@ -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 = begin
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
- Thread.current[:first_whitepace_removed] ||= 0
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 { #{compile_children} }"
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 binding.pry #raise "no template fragment supplied for: #{name}"
121
+ @fragments[name] or raise "no template fragment supplied for: #{name}"
122
122
  end
123
123
 
124
124
  private
@@ -20,9 +20,11 @@ module ExpressTemplates
20
20
  end
21
21
  end
22
22
 
23
+
23
24
  def compile
24
25
  %Q((#{@collection}.each_with_index.map do |#{@member}, #{@member}_index|#{compile_children}\nend).join)
25
26
  end
27
+
26
28
  end
27
29
  end
28
30
  end
@@ -10,7 +10,10 @@ module ExpressTemplates
10
10
  emits -> {
11
11
  div(style: 'display:none') {
12
12
  utf8_enforcer_tag
13
- method_tag(my[:id] || :post)
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