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.
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