express_templates 0.4.2 → 0.5.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/core_extensions/string.rb +25 -0
  3. data/lib/express_templates/components/capabilities/resourceful.rb +135 -0
  4. data/lib/express_templates/components/forms/express_form.rb +2 -15
  5. data/lib/express_templates/components/forms/form_component.rb +2 -6
  6. data/lib/express_templates/components/forms/form_support.rb +13 -0
  7. data/lib/express_templates/components/forms/option_support.rb +19 -5
  8. data/lib/express_templates/components/forms/select.rb +47 -9
  9. data/lib/express_templates/components/forms/select_collection.rb +57 -0
  10. data/lib/express_templates/components/forms.rb +3 -1
  11. data/lib/express_templates/components/tree_for.rb +9 -5
  12. data/lib/express_templates/version.rb +1 -1
  13. data/lib/express_templates.rb +1 -0
  14. data/test/components/capabilities/resourceful_test.rb +63 -0
  15. data/test/components/forms/express_form_test.rb +9 -9
  16. data/test/components/forms/select_test.rb +50 -3
  17. data/test/core_extensions/string_test.rb +20 -0
  18. data/test/dummy/log/test.log +9268 -63499
  19. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/-1ax0k6FO5drSUN6jbogg4G0JliUHLffvQUvzSePKxA.cache +1 -0
  20. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/28LzLLDYjhr3jmu0GxjZ-ms5Bol6JilDRXpg8Zgbjqs.cache +1 -0
  21. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/5AeGDyXQbv_BTj3PD4MJpNnGVwUxLsA8H1VcwB69_wE.cache +0 -0
  22. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/B0RgtiEmqwrCQuw4AnDa3fdENtJeBtO_TqtGJuWOeNs.cache +3 -0
  23. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/D-tMNp19G2zWOPPhnQRUm4K8DPa8SpKPfGALkkofTeE.cache +0 -0
  24. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/Pd5YOD1DAL7QtTnwETZYBCabg5DkCFgbjt4iuBOcSoY.cache +1 -0
  25. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/bSfZQxeyghTF4WIVnzGavxlg9afmSNuqcW6bA1Bm1OA.cache +0 -0
  26. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/fvD_ZNFRzd8Sc4PoTjyHPnkg4f7WMietFunnKqNjlvc.cache +2 -0
  27. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/hKbgrf5CbMO8pe9fCHc-rI5mp1ejAhivBfvfDxBNhQA.cache +0 -0
  28. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/kpvKX5UlhhoLJv-faeq7Ibv2KQh4ROjTiarh13gHuWI.cache +2 -0
  29. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/lfyrNtbNtwuTXAWlmPCKTS-D3FHoPTY6h53wnUN36-A.cache +0 -0
  30. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/lyDRhWNhfDw_YCCSbxQw_iOIV3eTfeAoX6mTREuVZSA.cache +3 -0
  31. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/p17lC0HAHCtk1ds_NHl9xhEyMtRjfQInw1c6fmFWguc.cache +0 -0
  32. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/qzU7DVwQZ7z6i6pYUpssYsAj0y33GN83B4O1bLvkW_4.cache +1 -0
  33. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/r56eI1z7iFKeySym_9zw5hVOPjb0d6IQPn5pAYTKk04.cache +2 -0
  34. data/test/dummy/tmp/cache/assets/test/sprockets/v3.0/rGWvoeLyiyqb813IXgfDpDxks23JQoLLZOa69bzKPE8.cache +1 -0
  35. metadata +42 -14
  36. data/test/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  37. data/test/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  38. data/test/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  39. data/test/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  40. data/test/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  41. data/test/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 30791598c725b41c362d6f436e51342db19abea2
4
- data.tar.gz: 139fc021df9d57161f27240a020826f203aebc40
3
+ metadata.gz: 7e3c4b0da15f9c3a8e15afd1cd406974c6a77830
4
+ data.tar.gz: 4fcaadfa00a739490b71b7a9539778aa3a7019e8
5
5
  SHA512:
6
- metadata.gz: 4889d57691fbb9a2f787381e42f89bcba50dd084381721003520e2aed39a11514befe3899587ffcab4192f7f2e333f39277681c30ee9c7cdd998c19dea4913d4
7
- data.tar.gz: 6d4623073cf1f498a4d542637f8a54331cfe7fda2088e33cf9cf05907835875175751a7a19e71adc9965f18f604b5cd450e753d6b4e2824fe5e810bdcea46717
6
+ metadata.gz: 4ad45dd634f82597ad578c21047556a4770ad76f05b7b92e344fbbde27993dc69a7ff1d53dd3c0c116e9dadc0f444633ea6dc325cf1a7cbcb596179946bea615
7
+ data.tar.gz: 1a67f14a59f8eac2b61a840376c0ff2ab1607a2e41533c6fa1543c5caa1ce7811a3d2d0b7b4ca3a66b745d8d38e2cfd99efafbf94d78a056284b187b163ea72b
@@ -0,0 +1,25 @@
1
+ # We use Hash#inspect in ExpressTemplates::Markup::Wrapper to reproduce arguments
2
+ # to helpers in the Rails view.
3
+ #
4
+ # Hash#inspect calls #inspect on values and keys. This allows us to
5
+ # place the resulting string into the view code with a simple substitution
6
+ # or concatenation.
7
+ #
8
+ # In special cases, however, we might want the argument or one of its keys
9
+ # or values to be the result of evaluating of a ruby expression in the view
10
+ # that does not itself return a String. The result of #inspect
11
+ # is normally a quoted and escaped string which would evaluate to a string
12
+ # in the view. What we want is a simple string of code. Here we provide
13
+ # a method that overrides #inspect in the strings eigenclass to strip off
14
+ # enclosing quotes or unwanted escapes. It shouldn't bother anybody and
15
+ # it keeps us from doing messy things elsewhere for now.
16
+ class String
17
+ def to_view_code
18
+ class << self
19
+ def inspect
20
+ super.gsub(/^"(.*)"$/,'\1')
21
+ end
22
+ end
23
+ return self
24
+ end
25
+ end
@@ -0,0 +1,135 @@
1
+ module ExpressTemplates
2
+ module Components
3
+ module Capabilities
4
+
5
+ module Resourceful
6
+ def namespace
7
+ @config[:namespace] || infer_namespace
8
+ end
9
+
10
+ def path_prefix
11
+ @config[:path_prefix] || infer_path_prefix
12
+ end
13
+
14
+ def resource_class
15
+ resource_class = @config[:resource_class] || _namespaced_resource_class
16
+ resource_class.constantize
17
+ end
18
+
19
+ private
20
+
21
+ def _namespaced_resource_class
22
+ if namespace
23
+ "#{namespace}/#{resource_name}".classify
24
+ else
25
+ resource_name.classify
26
+ end
27
+ end
28
+
29
+ def infer_namespace
30
+ expander = @args.last
31
+ if expander.try(:template)
32
+ path_parts = expander.template.virtual_path.split('/')
33
+
34
+ case
35
+ when path_parts.size == 4
36
+ path_parts.first
37
+ when path_parts.size == 3
38
+ mod = path_parts.first.classify.constantize
39
+ if mod.const_defined?(:Engine)
40
+ path_parts.first
41
+ else
42
+ nil
43
+ end
44
+ else
45
+ nil
46
+ end
47
+ else
48
+ nil
49
+ end
50
+ end
51
+
52
+ def infer_path_prefix
53
+ expander = @args.last
54
+ if expander.try(:template)
55
+ path_parts = expander.template.virtual_path.split('/')
56
+
57
+ case
58
+ when path_parts.size == 4
59
+ path_parts[1]
60
+ when path_parts.size == 3
61
+ mod = path_parts.first.classify.constantize
62
+ if mod.const_defined?(:Engine)
63
+ nil
64
+ else
65
+ path_parts.first
66
+ end
67
+ else
68
+ nil
69
+ end
70
+ else
71
+ nil
72
+ end
73
+ end
74
+
75
+ # TODO: this can now be inferred from the template.virtual_path
76
+ # if not supplied...
77
+ def resource_name
78
+ @config[:id].to_s.singularize
79
+ end
80
+
81
+ def collection_member_name
82
+ resource_name
83
+ end
84
+
85
+ def collection_name
86
+ collection_member_name.pluralize
87
+ end
88
+
89
+ def collection_var
90
+ "@#{collection_name}".to_sym
91
+ end
92
+
93
+ def collection
94
+ @config[:collection] || collection_var
95
+ end
96
+
97
+ def collection_path
98
+ if @config[:collection_path]
99
+ @config[:collection_path]
100
+ else
101
+ "#{collection_name_with_prefix}_path"
102
+ end
103
+ end
104
+
105
+ def collection_name_with_prefix
106
+ if path_prefix
107
+ "#{path_prefix}_#{collection_name}"
108
+ else
109
+ collection_name
110
+ end
111
+ end
112
+
113
+ def resource_path(ivar=false)
114
+ if @config[:resource_path]
115
+ @config[:resource_path]
116
+ else
117
+ "#{resource_name_with_path_prefix}_path(#{ivar ? '@' : ''}#{resource_name})"
118
+ end
119
+ end
120
+
121
+ def resource_name_with_path_prefix
122
+ if path_prefix
123
+ "#{path_prefix}_#{resource_name}"
124
+ else
125
+ resource_name
126
+ end
127
+ end
128
+
129
+ def attributes
130
+ resource_class.columns
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -4,6 +4,8 @@ module ExpressTemplates
4
4
  class ExpressForm < Base
5
5
  include Capabilities::Configurable
6
6
  include Capabilities::Parenting
7
+ include Capabilities::Resourceful
8
+ include Forms::FormSupport
7
9
 
8
10
  emits -> {
9
11
  form( form_args ) {
@@ -20,16 +22,6 @@ module ExpressTemplates
20
22
  @config[:method]
21
23
  end
22
24
 
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
25
 
34
26
  def form_args
35
27
  # there are no put/patch emthods in HTML5, so we have to enforce post
@@ -52,11 +44,6 @@ module ExpressTemplates
52
44
  (@config[:resource_name] || @config[:id]).to_s
53
45
  end
54
46
 
55
- def namespace
56
- @config[:namespace]
57
- end
58
-
59
-
60
47
  private
61
48
 
62
49
  def _modifying_resource?
@@ -20,11 +20,7 @@ module ExpressTemplates
20
20
  end
21
21
 
22
22
  def resource_class
23
- if namespace = parent_form.namespace
24
- "#{namespace}/#{resource_name}".classify
25
- else
26
- resource_name.classify
27
- end
23
+ parent_form.resource_class
28
24
  end
29
25
 
30
26
  # Return the name attribute for the lable
@@ -34,7 +30,7 @@ module ExpressTemplates
34
30
 
35
31
  # Return the text content for the label
36
32
  def label_text
37
- @options[:label] || field_name.titleize
33
+ @config[:label] || field_name.titleize
38
34
  end
39
35
 
40
36
  # Return the field_name as a string. This taken from the first argument
@@ -0,0 +1,13 @@
1
+ module ExpressTemplates
2
+ module Components
3
+ module Forms
4
+ module FormSupport
5
+
6
+ def form_action
7
+ @config[:action] || "{{@#{resource_name}.try(:persisted?) ? #{resource_path(ivar: true)} : #{collection_path}}}"
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -5,12 +5,18 @@ module ExpressTemplates
5
5
  # on the field and an means of loading the collection for supplying
6
6
  # options to the user.
7
7
  module OptionSupport
8
+
9
+ def has_many_through_association
10
+ reflection = resource_class.reflect_on_association(field_name.to_sym)
11
+ return reflection if reflection && reflection.macro.eql?(:has_many) && reflection.options.keys.include?(:through)
12
+ end
13
+
8
14
  # Reflect on any association and return it if the association type
9
15
  # is :belongs_to. Returns false if the association is not :belongs_to.
10
16
  # Returns nil if there was a problem reflecting.
11
17
  def belongs_to_association
12
18
  # assumes the belongs_to association uses <name>_id
13
- reflection = resource_class.constantize.reflect_on_association(field_name.gsub(/_id$/, '').to_sym)
19
+ reflection = resource_class.reflect_on_association(field_name.gsub(/_id$/, '').to_sym)
14
20
  if reflection && reflection.macro.eql?(:belongs_to)
15
21
  return reflection
16
22
  end
@@ -19,9 +25,13 @@ module ExpressTemplates
19
25
  # Provide ActiveRecord code to load the associated collection as
20
26
  # options for display.
21
27
  def related_collection
22
- reflection = belongs_to_association
28
+ reflection = belongs_to_association || has_many_through_association
23
29
  if reflection && !reflection.polymorphic?
24
- "#{reflection.klass}.all.select(:#{option_value_method}, :#{option_name_method}).order(:#{option_name_method})"
30
+ if cols.detect {|column| column.name.eql?('name') }
31
+ "#{reflection.klass}.all.select(:#{option_value_method}, :#{option_name_method}).order(:#{option_name_method})"
32
+ else
33
+ "#{reflection.klass}.all.sort_by(&:#{option_name_method})"
34
+ end
25
35
  end
26
36
  end
27
37
 
@@ -31,10 +41,14 @@ module ExpressTemplates
31
41
  :id
32
42
  end
33
43
 
44
+ def cols
45
+ @cols ||= (belongs_to_association||has_many_through_association).klass.columns
46
+ end
47
+
34
48
  def option_name_method
35
- cols = belongs_to_association.klass.columns
36
49
  @option_name_method ||=
37
- if cols.detect {|column| column.name.eql?('name') }
50
+ if cols.detect {|column| column.name.eql?('name') } ||
51
+ resource_class.instance_methods.include?(:name)
38
52
  :name
39
53
  else
40
54
  if string_col = cols.detect {|column| column.type.eql?(:string) }
@@ -21,15 +21,26 @@ module ExpressTemplates
21
21
  emits -> {
22
22
  div(class: field_wrapper_class) {
23
23
  label_tag(label_name, label_text)
24
- select_tag(field_name_attribute, select_options, field_options)
24
+ select_tag(*select_tag_args)
25
25
  }
26
26
  }
27
27
 
28
+ def select_tag_args
29
+ args = [field_name_attribute, select_options, field_options]
30
+ args << html_options unless html_options.nil? or html_options.empty?
31
+ args
32
+ end
33
+
28
34
  # Returns the options which will be supplied to the select_tag helper.
29
35
  def select_options
30
- options_specified = [Array, Hash].include?(@args.second.class) && @args.size > 2
36
+ options_specified = [Array, Hash, Proc].include?(@args.second.class) && @args.size > 2
31
37
  if options_specified
32
- options = @args.second
38
+ if @args.second.respond_to?(:source) # can be a proc
39
+ options = "#{@args.second.source}.call()"
40
+ else
41
+ options = @args.second
42
+ end
43
+
33
44
  else
34
45
  options = "@#{resource_var}.class.distinct(:#{field_name}).pluck(:#{field_name})"
35
46
  end
@@ -40,6 +51,8 @@ module ExpressTemplates
40
51
  else
41
52
  "{{options_from_collection_for_select(#{related_collection}, :id, :#{option_name_method}, @#{resource_name}.#{field_name})}}"
42
53
  end
54
+ elsif has_many_through_association
55
+ "{{options_from_collection_for_select(#{related_collection}, :id, :#{option_name_method}, @#{resource_name}.#{field_name}.map(&:id))}}"
43
56
  else
44
57
  if selection = field_options.delete(:selected)
45
58
  "{{options_for_select(#{options}, \"#{selection}\")}}"
@@ -49,18 +62,43 @@ module ExpressTemplates
49
62
  end
50
63
  end
51
64
 
65
+ def field_name_attribute
66
+ if has_many_through_association
67
+ "#{resource_name.singularize}[#{field_name.singularize}_ids]"
68
+ else
69
+ super
70
+ end
71
+ end
72
+
52
73
  def field_options
53
74
  # If field_otions is omitted the Expander will be
54
75
  # in last or 3rd position and we don't want that
55
76
  defaults = {include_blank: true}
56
- field_options = if @args.size > 3 && @args[2].is_a?(Hash)
57
- @args[2]
58
- else
59
- {}
60
- end
61
- defaults.merge(field_options)
77
+ defaults.merge(supplied_field_options.reject {|k,v| k.eql?(:select2)})
62
78
  end
63
79
 
80
+ def html_options
81
+ supplied_html_options
82
+ end
83
+
84
+ protected
85
+
86
+ def supplied_field_options
87
+ if @args.size > 3 && @args[2].is_a?(Hash)
88
+ @args[2]
89
+ else
90
+ {}
91
+ end
92
+ end
93
+
94
+ def supplied_html_options
95
+ if @args.size > 4 && @args[3].is_a?(Hash)
96
+ @args[3]
97
+ else
98
+ {}
99
+ end
100
+ end
101
+
64
102
  end
65
103
  end
66
104
  end
@@ -0,0 +1,57 @@
1
+ module ExpressTemplates
2
+ module Components
3
+ module Forms
4
+ # Provides a form Select component based on the Rails *select_tag* helper.
5
+ # Parameters:
6
+ # field_name, select_options, helper_options
7
+ #
8
+ # Select options may be specified as an Array or Hash which will be
9
+ # supplied to the *options_for_select* helper.
10
+ #
11
+ class SelectCollection < Select
12
+
13
+ emits -> {
14
+ div(class: field_wrapper_class) {
15
+ label_tag(label_name, label_text)
16
+
17
+ # need this because the collection_select helper does not provide
18
+ # the hidden_field_tag trick (see rails api docs for select)
19
+ hidden_field_tag(multi_field_name, '')
20
+
21
+ collection_select(*collection_select_tag_args)
22
+ }
23
+ }
24
+
25
+ def collection_select_tag_args
26
+ [ resource_name,
27
+ multi_field_name,
28
+ select_options, :id, :name,
29
+ field_options,
30
+ html_options ]
31
+ end
32
+
33
+ def field_options
34
+ selected_options_ruby = "@#{resource_name}.#{multi_field_name}".to_view_code
35
+ super.merge(include_blank: false, selected: selected_options_ruby)
36
+ end
37
+
38
+ def html_options
39
+ (super||{}).merge(multiple: true)
40
+ end
41
+
42
+ def multi_field_name
43
+ if has_many_through_association
44
+ "#{field_name.singularize}_ids"
45
+ else
46
+ raise "Only use select_collection for has_many :through. #{field_name} is not has_many :through"
47
+ end
48
+ end
49
+
50
+
51
+ def select_options
52
+ "{{#{related_collection}}}"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -5,11 +5,13 @@ module ExpressTemplates
5
5
  end
6
6
  end
7
7
 
8
+ require 'express_templates/components/forms/form_support'
8
9
  require 'express_templates/components/forms/express_form'
9
10
  require 'express_templates/components/forms/form_component'
10
11
  require 'express_templates/components/forms/option_support'
11
12
  require 'express_templates/components/forms/submit'
12
13
  require 'express_templates/components/forms/select'
14
+ require 'express_templates/components/forms/select_collection'
13
15
  require 'express_templates/components/forms/radio'
14
16
  require 'express_templates/components/forms/checkbox'
15
- require 'express_templates/components/forms/basic_fields'
17
+ require 'express_templates/components/forms/basic_fields'
@@ -11,9 +11,9 @@ module ExpressTemplates
11
11
  # Example:
12
12
  #
13
13
  # ```ruby
14
- # tree_for(:roles) do |role|
15
- # role.name
16
- # end
14
+ # tree_for(:roles) {
15
+ # "{{role.name}}"
16
+ # }
17
17
  # ```
18
18
  #
19
19
  # If the view has an @roles variable with a Role having children,
@@ -59,12 +59,16 @@ module ExpressTemplates
59
59
  end
60
60
 
61
61
  def compile
62
- collection = _variablize(@options[:id])
62
+ collection = if @options[:collection]
63
+ "#{@options[:collection].source}.call()"
64
+ else
65
+ _variablize(@options[:id])
66
+ end
63
67
  member = @options[:id].to_s.singularize
64
68
  return 'ExpressTemplates::Components::TreeFor.render_in(self) {
65
69
  node_renderer = '+node_renderer.gsub(/node/, member)+'
66
70
  ExpressTemplates::Indenter.for(:tree) do |ws, wsnl|
67
- "#{ws}<ul id=\"roles\" class=\"roles tree\">" +
71
+ "#{ws}<ul id=\"'+@options[:id]+'\" class=\"'+@options[:id]+' tree\">" +
68
72
  '+collection+'.map do |'+member+'|
69
73
  node_renderer.call('+member+', node_renderer)
70
74
  end.join +
@@ -1,3 +1,3 @@
1
1
  module ExpressTemplates
2
- VERSION = "0.4.2"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -1,5 +1,6 @@
1
1
  module ExpressTemplates
2
2
  require 'core_extensions/proc'
3
+ require 'core_extensions/string'
3
4
  require 'express_templates/indenter'
4
5
  require 'express_templates/macro'
5
6
  require 'express_templates/markup'
@@ -0,0 +1,63 @@
1
+ require 'test_helper'
2
+
3
+ module AdminModule
4
+ module Engine
5
+ end
6
+ class SmartThing
7
+ include ExpressTemplates::Components::Capabilities::Resourceful
8
+
9
+ attr_accessor :virtual_path
10
+
11
+ def initialize(virtual_path, config = {})
12
+ @virtual_path = virtual_path
13
+ @config = config
14
+ @args = [self]
15
+ end
16
+
17
+ def template
18
+ self
19
+ end
20
+ end
21
+ end
22
+
23
+ module Admin; end
24
+
25
+ class FooBar; end
26
+
27
+ class Something; end
28
+
29
+ module ExpressTemplates
30
+
31
+ class ResourcefulTest < ActiveSupport::TestCase
32
+ test 'infers namespace and path prefix within an engine and scope' do
33
+ smart_thing = AdminModule::SmartThing.new('admin_module/admin/something/index')
34
+ assert_equal 'admin_module', smart_thing.namespace
35
+ assert_equal 'admin', smart_thing.path_prefix
36
+ end
37
+
38
+ test 'infers a namespace and no prefix within an engine' do
39
+ # if defined? ExpressFoo::Engine
40
+ smart_thing = AdminModule::SmartThing.new('admin_module/something/index')
41
+ assert_equal 'admin_module', smart_thing.namespace
42
+ assert_equal nil, smart_thing.path_prefix
43
+ end
44
+
45
+ test 'no namespace, infers prefix within a scope within an app' do
46
+ # else of case above
47
+ smart_thing = AdminModule::SmartThing.new('admin/something/index')
48
+ assert_equal nil, smart_thing.namespace
49
+ assert_equal 'admin', smart_thing.path_prefix
50
+ end
51
+
52
+ test 'no namespace, no prefix within an app' do
53
+ smart_thing = AdminModule::SmartThing.new('somethings/index')
54
+ assert_equal nil, smart_thing.namespace
55
+ assert_equal nil, smart_thing.path_prefix
56
+ end
57
+
58
+ test "#resource_class returns resource_class option if specified" do
59
+ assert_equal FooBar, AdminModule::SmartThing.new('somethings/index', resource_class: 'FooBar').resource_class
60
+ assert_equal Something, AdminModule::SmartThing.new('somethings/index', id: :something).resource_class
61
+ end
62
+ end
63
+ end
@@ -72,16 +72,16 @@ class ExpressFormTest < ActiveSupport::TestCase
72
72
  end
73
73
 
74
74
  test "#form_action uses url helpers" do
75
- assert_equal "{{foos_path}}", express_form.new(:foo).form_action
75
+ assert_equal "{{@foo.try(:persisted?) ? foo_path(@foo) : foos_path}}", express_form.new(:foo).form_action
76
76
  end
77
77
 
78
78
  test "#form_action uses correct path helper for update/patch" do
79
- assert_equal "{{foo_path(@foo)}}", express_form.new(:foo, method: :put).form_action
79
+ assert_equal "{{@foo.try(:persisted?) ? foo_path(@foo) : foos_path}}", express_form.new(:foo, method: :put).form_action
80
80
  end
81
81
 
82
82
  test "simplest_form uses form_action for the action" do
83
83
  form_open_tag = compile_simplest_form.match(/<form[^>]*>/)[0]
84
- assert_match 'action=\"#{resources_path}\"', form_open_tag
84
+ assert_match 'action=\"#{@resource.try(:persisted?) ? resource_path(@resource) : resources_path}\"', form_open_tag
85
85
  end
86
86
 
87
87
  test "express_form default method is POST" do
@@ -99,10 +99,10 @@ class ExpressFormTest < ActiveSupport::TestCase
99
99
  assert_equal 'foo', expanded_nodes.first.resource_name
100
100
  end
101
101
 
102
- test "express_form has a namespace option with nil default" do
103
- form = ExpressTemplates::Components::Forms::ExpressForm
104
- assert_nil form.new(:person).namespace
105
- assert_equal 'express_engine', form.new(:person, namespace: 'express_engine').namespace
106
- end
102
+ # test "express_form has a namespace option with nil default" do
103
+ # form = ExpressTemplates::Components::Forms::ExpressForm
104
+ # assert_nil form.new(:person).namespace
105
+ # assert_equal 'express_engine', form.new(:person, namespace: 'express_engine').namespace
106
+ # end
107
107
 
108
- end
108
+ end
@@ -52,16 +52,31 @@ class SelectTest < ActiveSupport::TestCase
52
52
  [OpenStruct.new(name: 'id'), OpenStruct.new(name: 'name')]
53
53
  end
54
54
  end
55
+ class ::Tagging
56
+ def self.columns
57
+ [OpenStruct.new(name: 'id'), OpenStruct.new(name: 'name')]
58
+ end
59
+ end
55
60
  class ::Person
56
61
  def self.reflect_on_association(name)
57
62
  if name.eql? :gender
58
- dummy_association = Object.new
59
- class << dummy_association
63
+ dummy_belongs_to_association = Object.new
64
+ class << dummy_belongs_to_association
60
65
  def macro ; :belongs_to ; end
61
66
  def klass ; ::Gender ; end
62
67
  def polymorphic? ; false ; end
63
68
  end
64
- return dummy_association
69
+ return dummy_belongs_to_association
70
+ end
71
+ if name.eql? :taggings
72
+ dummy_has_many_through_association = Object.new
73
+ class << dummy_has_many_through_association
74
+ def macro ; :has_many ; end
75
+ def klass ; ::Tagging ; end
76
+ def options ; {:through => :peron_tags} ; end
77
+ def polymorphic? ; false ; end
78
+ end
79
+ return dummy_has_many_through_association
65
80
  end
66
81
  end
67
82
  end
@@ -96,4 +111,36 @@ class SelectTest < ActiveSupport::TestCase
96
111
  assert_no_match 'include_blank: true', ExpressTemplates.compile(&fragment)
97
112
  end
98
113
 
114
+ test "select multiple: true if passed multiple true" do
115
+ fragment = -> {
116
+ express_form(:person) {
117
+ select :taggings, nil, include_blank: false, multiple: true
118
+ }
119
+ }
120
+ assert_match 'multiple: true', ExpressTemplates.compile(&fragment)
121
+ end
122
+
123
+ test "select multiple gets options from associated has_many_through collection" do
124
+ fragment = -> {
125
+ express_form(:person) {
126
+ select :taggings, nil, include_blank: false, multiple: true
127
+ }
128
+ }
129
+ assert_match 'tagging_ids', ExpressTemplates.compile(&fragment)
130
+ assert_match 'options_from_collection_for_select(Tagging.all.select(:id, :name).order(:name), :id, :name, @person.taggings.map(&:id))',
131
+ ExpressTemplates.compile(&fragment)
132
+ end
133
+
134
+ test "select_collection uses collection_select" do
135
+ fragment = -> {
136
+ express_form(:person) {
137
+ select_collection :taggings
138
+ }
139
+ }
140
+ assert_match 'tagging_ids', ExpressTemplates.compile(&fragment)
141
+ assert_match 'collection_select("person", "tagging_ids", Tagging.all.select(:id, :name).order(:name), :id, :name, {:include_blank=>false, :selected=>@person.tagging_ids}, multiple: true)',
142
+ ExpressTemplates.compile(&fragment)
143
+ end
144
+
145
+
99
146
  end