formize 0.1.1 → 1.0.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 (158) hide show
  1. data/README.rdoc +6 -40
  2. data/VERSION +1 -1
  3. data/lib/assets/javascripts/formize-autocomplete.js +11 -0
  4. data/lib/assets/javascripts/formize-behave.js +52 -0
  5. data/lib/assets/javascripts/formize-datepicker.js +115 -0
  6. data/lib/assets/javascripts/formize-dependents.js +50 -0
  7. data/lib/assets/javascripts/formize-dialog.js +123 -0
  8. data/lib/assets/javascripts/formize-observe.js +24 -0
  9. data/lib/assets/javascripts/formize-popover-form.js +32 -0
  10. data/lib/assets/javascripts/formize-timepicker.js +57 -0
  11. data/lib/assets/javascripts/formize-toggle.js +24 -0
  12. data/lib/assets/javascripts/formize-unroll.js +30 -0
  13. data/lib/assets/javascripts/formize.js +10 -436
  14. data/lib/formize.rb +5 -65
  15. data/lib/formize/action_controller.rb +75 -46
  16. data/lib/formize/generator.rb +160 -547
  17. data/lib/formize/helpers/form_helper.rb +40 -38
  18. data/lib/formize/rails/railtie.rb +1 -1
  19. data/test/ci/Gemfile.rails-3.1 +9 -0
  20. data/test/ci/Gemfile.rails-3.2 +9 -0
  21. data/vendor/assets/javascripts/jquery-ui-timepicker-addon.js +1882 -0
  22. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-af.js +20 -0
  23. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-bg.js +20 -0
  24. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-ca.js +20 -0
  25. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-cs.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-cs.js} +5 -2
  26. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-de.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-de.js} +5 -2
  27. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-el.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-el.js} +5 -2
  28. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-es.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-es.js} +5 -2
  29. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-et.js +20 -0
  30. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-fi.js +20 -0
  31. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-fr.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-fr.js} +5 -2
  32. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-gl.js +20 -0
  33. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-he.js +20 -0
  34. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-hu.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-hu.js} +5 -2
  35. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-id.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-id.js} +5 -2
  36. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-it.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-it.js} +5 -2
  37. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-ja.js +20 -0
  38. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-ko.js +20 -0
  39. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-lt.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-lt.js} +5 -2
  40. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-nl.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-nl.js} +5 -2
  41. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-no.js +20 -0
  42. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-pl.js +20 -0
  43. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-pt-BR.js +20 -0
  44. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-pt.js +20 -0
  45. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-ro.js +20 -0
  46. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-ru.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-ru.js} +7 -4
  47. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-sk.js +20 -0
  48. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-sv.js +20 -0
  49. data/{lib/assets/javascripts/locales/jquery.ui.timepicker-tr.js → vendor/assets/javascripts/locales/jquery-ui-timepicker-tr.js} +6 -3
  50. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-uk.js +20 -0
  51. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-vi.js +20 -0
  52. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-zh-CN.js +20 -0
  53. data/vendor/assets/javascripts/locales/jquery-ui-timepicker-zh-TW.js +20 -0
  54. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-af.js +1 -1
  55. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-ar-DZ.js +23 -0
  56. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-ar.js +23 -0
  57. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-az.js +4 -4
  58. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-be.js +23 -0
  59. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-bg.js +24 -0
  60. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-bs.js +6 -6
  61. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-ca.js +23 -0
  62. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-cs.js +4 -4
  63. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-cy-GB.js +23 -0
  64. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-da.js +23 -0
  65. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-de-CH.js +0 -0
  66. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-de.js +4 -4
  67. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-el.js +2 -2
  68. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-en-AU.js +23 -0
  69. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-en-GB.js +1 -1
  70. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-en-NZ.js +23 -0
  71. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-eo.js +3 -3
  72. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-es.js +6 -6
  73. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-et.js +3 -3
  74. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-eu.js +23 -0
  75. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-fa.js +59 -0
  76. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-fi.js +23 -0
  77. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-fo.js +4 -4
  78. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-fr-CA.js +23 -0
  79. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-fr-CH.js +2 -2
  80. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-fr.js +25 -0
  81. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-gl.js +23 -0
  82. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-he.js +5 -5
  83. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-hi.js +23 -0
  84. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-hr.js +4 -4
  85. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-hu.js +7 -7
  86. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-hy.js +3 -3
  87. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-id.js +3 -3
  88. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-is.js +23 -0
  89. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-it.js +3 -3
  90. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-ja.js +4 -4
  91. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-ka.js +21 -0
  92. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-kk.js +23 -0
  93. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-km.js +23 -0
  94. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-ko.js +8 -8
  95. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-ky.js +24 -0
  96. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-lb.js +23 -0
  97. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-lt.js +3 -3
  98. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-lv.js +1 -1
  99. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-mk.js +23 -0
  100. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-ml.js +23 -0
  101. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-ms.js +3 -3
  102. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-nb.js +22 -0
  103. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-nl-BE.js +23 -23
  104. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-nl.js +5 -5
  105. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-nn.js +22 -0
  106. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-no.js +23 -0
  107. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-pl.js +4 -4
  108. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-pt-BR.js +7 -7
  109. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-pt.js +22 -0
  110. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-rm.js +21 -0
  111. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-ro.js +4 -4
  112. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-ru.js +4 -4
  113. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-sk.js +4 -4
  114. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-sl.js +6 -6
  115. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-sq.js +3 -3
  116. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-sr-SR.js +3 -3
  117. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-sr.js +3 -3
  118. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-sv.js +23 -0
  119. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-ta.js +1 -1
  120. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-th.js +5 -5
  121. data/vendor/assets/javascripts/locales/jquery.ui.datepicker-tj.js +23 -0
  122. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-tr.js +2 -2
  123. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-uk.js +5 -4
  124. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-vi.js +3 -3
  125. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-zh-CN.js +4 -4
  126. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-zh-HK.js +4 -4
  127. data/{lib → vendor}/assets/javascripts/locales/jquery.ui.datepicker-zh-TW.js +5 -5
  128. metadata +127 -92
  129. data/lib/assets/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  130. data/lib/assets/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  131. data/lib/assets/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  132. data/lib/assets/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  133. data/lib/assets/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  134. data/lib/assets/images/ui-bg_glass_75_ffffff_1x400.png +0 -0
  135. data/lib/assets/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  136. data/lib/assets/images/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
  137. data/lib/assets/images/ui-icons_222222_256x240.png +0 -0
  138. data/lib/assets/images/ui-icons_2e83ff_256x240.png +0 -0
  139. data/lib/assets/images/ui-icons_454545_256x240.png +0 -0
  140. data/lib/assets/images/ui-icons_888888_256x240.png +0 -0
  141. data/lib/assets/images/ui-icons_cd0a0a_256x240.png +0 -0
  142. data/lib/assets/javascripts/jquery.ui.formize.js +0 -380
  143. data/lib/assets/javascripts/jquery.ui.timepicker.js +0 -1060
  144. data/lib/assets/javascripts/locales/jquery.ui.datepicker-ar.js +0 -24
  145. data/lib/assets/javascripts/locales/jquery.ui.datepicker-bg.js +0 -24
  146. data/lib/assets/javascripts/locales/jquery.ui.datepicker-ca.js +0 -23
  147. data/lib/assets/javascripts/locales/jquery.ui.datepicker-da.js +0 -23
  148. data/lib/assets/javascripts/locales/jquery.ui.datepicker-eu.js +0 -23
  149. data/lib/assets/javascripts/locales/jquery.ui.datepicker-fa.js +0 -23
  150. data/lib/assets/javascripts/locales/jquery.ui.datepicker-fi.js +0 -23
  151. data/lib/assets/javascripts/locales/jquery.ui.datepicker-fr.js +0 -23
  152. data/lib/assets/javascripts/locales/jquery.ui.datepicker-is.js +0 -23
  153. data/lib/assets/javascripts/locales/jquery.ui.datepicker-no.js +0 -23
  154. data/lib/assets/javascripts/locales/jquery.ui.datepicker-sv.js +0 -23
  155. data/lib/assets/javascripts/locales/jquery.ui.timepicker-et.js +0 -17
  156. data/lib/assets/javascripts/locales/jquery.ui.timepicker-vi.js +0 -17
  157. data/lib/assets/stylesheets/formize.css +0 -20
  158. data/lib/assets/stylesheets/jquery-ui.css +0 -586
data/lib/formize.rb CHANGED
@@ -15,6 +15,10 @@ module Formize
15
15
  end
16
16
  end
17
17
 
18
+ # Module used to "stock" labelling methods for items
19
+ module CompiledLabels
20
+ end
21
+
18
22
  extend ActiveSupport::Autoload
19
23
 
20
24
  def self.configure(name, value = nil) # :nodoc:
@@ -50,71 +54,7 @@ module Formize
50
54
  # How many select options can be displayed before to become a +unroll+
51
55
  configure :select_count_max, 7
52
56
 
53
-
54
-
55
-
56
-
57
- # Compile a monolithic javascript file formize.js with all included
58
- def self.compile!
59
- # js_dir = Pathname.new(File.dirname(__FILE__)).join("..", "assets", "javascripts")
60
- js_dir = File.join(File.dirname(__FILE__), "assets", "javascripts")
61
- File.open("#{js_dir}/formize.js", "wb") do |js|
62
-
63
- js.write "(function($) {\n"
64
- for file in Dir.glob("#{js_dir}/locales/jquery.ui.datepicker*.js").sort
65
- File.open(file, "rb") do |f|
66
- source = f.read
67
- source.gsub!("jQuery(function($){", '')
68
- source.gsub!(/\/\*[^\*\/]*\*\//, '')
69
- source.gsub!(/\$\.datepicker\.setDefaults\(\$\.datepicker\.regional\[\'\w\w(\-\w\w)?\'\]\)\;/, '')
70
- source.gsub!("});", '')
71
- source = source.strip.split(/\n/).collect{|l| l.strip}.join(" ").strip.sub(/^.*\$/, '$')
72
- js.write source
73
- # js.write f.read
74
- js.write "\n"
75
- end
76
- end
77
- js.write "})(jQuery);\n"
78
-
79
-
80
- # File.open("#{js_dir}/jquery.ui.timepicker.js", "rb") do |f|
81
- # js.write f.read
82
- # js.write "\n"
83
- # end
84
-
85
-
86
- # js.write "(function($) {\n"
87
- # for file in Dir.glob("#{js_dir}/locales/jquery.ui.timepicker*.js").sort
88
- # File.open(file, "rb") do |f|
89
- # source = f.read
90
- # source.gsub!("(function($) {", '')
91
- # source.gsub!(/\/\*[^\*\/]*\*\//, '')
92
- # source.gsub!(/\$\.timepicker\.setDefaults\(\$\.timepicker\.regional\[\'\w\w(\-\w\w)?\'\]\)\;/, '')
93
- # source.gsub!("})(jQuery)\;", '')
94
- # source = source.strip.split(/\n/).collect{ |l| l.gsub(/^\s*/, '')}.join(" ").sub(/^.*\$/, '$')
95
- # js.write source
96
- # js.write "\n"
97
- # end
98
- # end
99
- # js.write "})(jQuery);\n"
100
-
101
-
102
- File.open("#{js_dir}/jquery.ui.formize.js", "rb") do |f|
103
- js.write f.read
104
- js.write "\n"
105
- end
106
- end
107
-
108
- end
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
- autoload :Definition
117
57
  autoload :Helpers
118
- autoload :ActionController
119
58
  autoload :Generator
59
+ autoload :ActionController
120
60
  end
@@ -18,11 +18,13 @@ module Formize
18
18
  response.headers["X-Return-Code"] = "success"
19
19
  response.headers["X-Saved-Record-Id"] = resource.id.to_s
20
20
  format.html { params[:dialog] ? head(status) : redirect_to(options[:url] || resource) }
21
- format.xml { render :xml => resource, :status => status, :location => resource }
21
+ format.json { render :json => resource.to_json, :status => status, :location => resource}
22
+ format.xml { render :xml => resource.to_xml, :status => status, :location => resource }
22
23
  else
23
24
  response.headers["X-Return-Code"] = "invalid"
24
25
  format.html { render :action => (resource.new_record? ? "new" : "edit")}
25
- format.xml { render :xml => resource.errors, :status => :unprocessable_entity }
26
+ format.json { render :json => resource.errors.to_json, :status => :unprocessable_entity }
27
+ format.xml { render :xml => resource.errors.to_xml, :status => :unprocessable_entity }
26
28
  end
27
29
  end
28
30
  end
@@ -31,51 +33,78 @@ module Formize
31
33
 
32
34
  module ClassMethods
33
35
 
34
- # Generates controller and view method working together
35
- # This methods allows to develop dynamic forms without using partials
36
- # and another action, only formize or formize_*.
37
- def formize(*args, &block)
38
- name, options = nil, {}
39
- name = args[0] if args[0].is_a? Symbol
40
- options = args[-1] if args[-1].is_a? Hash
41
- name ||= self.controller_name.to_sym
42
- model = (options[:model]||name).to_s.classify.constantize
43
- options[:controller_method_name] = "formize#{'_'+name.to_s if name != self.controller_name.to_sym}"
44
- options[:view_form_method_name] = "_#{self.controller_name}_formize_form_#{name}_tag"
45
- options[:view_fields_method_name] = "_#{self.controller_name}_formize_fields_#{name}_tag"
46
- options[:method_name] = options[:view_fields_method_name]
47
- options[:best_name] = "#{self.controller_name}#{'_'+name.to_s if name != self.controller_name.to_sym}"
48
- form = Formize::Definition::Form.new(name, model, options)
49
- if block_given?
50
- yield form
51
- else
52
- formize_by_default(form)
53
- end
54
- generator = Formize::Generator::Base.new(form, self)
55
- class_eval(generator.controller_code, "#{__FILE__}:#{__LINE__}")
56
- ActionView::Base.send(:class_eval, generator.view_code, "#{__FILE__}:#{__LINE__}")
57
- end
58
-
59
- private
60
-
61
- # Generates list of usable field
62
- def formize_by_default(form)
63
- form.field_set(:general) do |f|
64
- for column in form.model.columns
65
- next if column.name =~ /_count$/ or [:id, :created_at, :updated_at, :lock_version, :type, :creator_id, :updater_id].include?(column.name.to_sym)
66
- if column.name =~ /_id$/
67
- reflections = form.model.reflections.select{ |k, x| x.send(Formize.foreign_key).to_s == column.name.to_s }
68
- if reflections.size == 1
69
- f.field(column.name.gsub(/_id$/, ''), :choices=>:all, :source=>:foreign_class, :new=>true)
70
- # elsif reflections.size > 1 # AMBIGUITY
71
- # elsif reflections.size < 1 # NOTHING
72
- end
73
- else
74
- f.field(column.name)
75
- end
76
- end
36
+ # Generates a default action which is the resource for a unroll box.
37
+ # It generates an helper which takes in account selected columns for displaying.
38
+ # The label used to display items is based on the used columns. These columns can be
39
+ # used with I18n. The key used is: +views.unroll.<controller>.<action>+
40
+ #
41
+ # @macro [new] options_details
42
+ # @param [Hash] options Options to build controller action
43
+ # @option options [Array] :columns The columns which are used for search and display
44
+ # All the content columns are used by default.
45
+ # A column can be a Symbol/String with its name or Hash with keys (+:name+,
46
+ # +:filter+, +:interpolation_key+)
47
+ # @option options [Array,Hash] :conditions Default conditions used in the search query
48
+ # @option options [String, Hash, Array] :joins To make a join like in +find+
49
+ # @option options [Integer] :limit (80) Maximum count of items in results
50
+ # @option options [String] :partial Specify a partial for HTML autocompleter
51
+ # @option options [String] :filter ('%X%') Filter format used to build search query.
52
+ # Specific filters can be specified for each column.
53
+ #
54
+ # @overload search_for(name, model, options={})
55
+ # Defines a controller method +search_for_NAME+ which searches for records
56
+ # of the class +MODEL+.
57
+ # @param [Symbol] name Name of the datasource
58
+ # @param [String, Symbol] name Name of the model to use for searching
59
+ # @macro options_details
60
+ #
61
+ # @overload search_for(name, options={})
62
+ # Defines a controller method +search_for_NAME+ which searches for records
63
+ # of the class +NAME+.
64
+ # @param [Symbol] name
65
+ # Name of the datasource. This name is used to find the model name
66
+ # @macro options_details
67
+ #
68
+ # @overload search_for(options={})
69
+ # Defines a controller method +search_for+ which searches for records corresponding to the
70
+ # resource controller name. +OrdersController#search_for+ searches for orders.
71
+ # @macro options_details
72
+ #
73
+ # @example Search clients with Person model
74
+ # # app/controller/orders_controller.rb
75
+ # class OrdersController < ApplicationController
76
+ # ...
77
+ # search_for :clients, :person
78
+ # ...
79
+ # end
80
+ #
81
+ # @example Search all accounts where name contains search and number starts with search
82
+ # # app/controller/orders_controller.rb
83
+ # class PeopleController < ApplicationController
84
+ # ...
85
+ # search_for :accounts, :columns=>[:name, 'number:X%']
86
+ # ...
87
+ # end
88
+ #
89
+ # @example Search for orders among all others
90
+ # # app/controller/orders_controller.rb
91
+ # class OrdersController < ApplicationController
92
+ # ...
93
+ # search_for
94
+ # ...
95
+ # end
96
+ def search_for(*args)
97
+ options = args.delete_at(-1) if args[-1].is_a? Hash
98
+ name, model = args[0], args[1]
99
+ action_name = "#{__method__}#{'_'+name.to_s if name}"
100
+ model = model || name || controller_name
101
+ if [String, Symbol].include?(model.class)
102
+ model = model.to_s.classify.constantize
77
103
  end
78
- return form
104
+ return unless model.table_exists?
105
+ generator = Generator::Base.new(self, action_name, model, options)
106
+ class_eval(generator.controller_action, "#{__FILE__}:#{__LINE__}")
107
+ Formize::CompiledLabels.send(:class_eval, generator.item_label_code, "#{__FILE__}:#{__LINE__}")
79
108
  end
80
109
 
81
110
  end
@@ -1,494 +1,206 @@
1
1
  module Formize
2
+ module Generator
2
3
 
3
- # Permits to not quote text in inspect method
4
- class Code < String # :nodoc:
5
-
6
- def inspect
7
- self.to_s
8
- end
9
-
10
- end
4
+ class Column
5
+ attr_reader :name, :filter, :interpolation_key, :column
6
+
7
+ # @@count = 0
8
+
9
+ def initialize(model, name, options={})
10
+ @model = model
11
+ @name = name.to_s
12
+ @filter = options.delete(:filter) || "%X%"
13
+ @code = options.delete(:code)
14
+ @interpolation_key = options.delete(:interpolation_key) || @name.gsub(/\W/, '_')
15
+ klass = @model
16
+ @through = (options.delete(:through) || []).collect do |reflection|
17
+ unless klass.reflections[reflection.to_sym]
18
+ raise Exception.new("Model #{klass.name} has no reflections #{reflection.inspect}")
19
+ end
20
+ klass = klass.reflections[reflection.to_sym].class_name.constantize
21
+ reflection.to_sym
22
+ end || []
23
+ @column = foreign_model.columns_hash[@name]
24
+ end
25
+
26
+ def sql_name
27
+ return "#{foreign_model.table_name}.#{@name}"
28
+ end
29
+
30
+ def value_code(record='record')
31
+ code = "("
32
+ value = "(#{record}#{'.'+@through.join('.') unless @through.empty?}.#{name})"
33
+ @through.each_index do |i|
34
+ code << "#{record}.#{@through[0..i].join('.')}.nil? ? '' : "
35
+ end
36
+ code << if @code
37
+ @code.gsub(/RECORD/, record).gsub(/DATUM/, value)
38
+ elsif[:date, :datetime, :timestamp].include? self.type
39
+ "(#{value}.nil? ? '' : ::I18n.localize(#{value}))"
40
+ else
41
+ value
42
+ end
43
+ code << ")"
44
+ code << ".to_s" unless name.to_s == "count"
45
+ return code
46
+ end
11
47
 
12
- module Generator
13
- class Base
14
-
15
- attr_accessor :form, :elements, :record_name, :partial, :controller
48
+ def type
49
+ @column.type
50
+ end
16
51
 
17
- def initialize(form, controller)
18
- @form = form
19
- @controller = controller
20
- @record_name = @form.record_name
21
- @elements = form.all_elements
22
- @partials = @elements.select{|e| !e.depend_on.nil? or e.options[:new]}
52
+ def foreign_model
53
+ model = @model
54
+ for reflection in @through
55
+ model = model.reflections[reflection].class_name.constantize
56
+ end
57
+ return model
23
58
  end
24
59
 
60
+ end
61
+
25
62
 
26
- # Generates the controller method from a form object
27
- def controller_code
28
- code = "def #{form.controller_method_name}\n"
63
+ class Base
29
64
 
30
- # Mono_choice search/filter
31
- items = form.mono_choices
32
- if items.size > 0
33
- code << " if params[:unroll]\n"
34
- events = form.mono_choices.collect do |mono_choice|
35
- event = "if params[:unroll] == '#{mono_choice.html_id}'\n"
65
+ attr_accessor :action_name, :controller, :options
36
66
 
37
- for depended in mono_choice.dependeds
38
- df = form.all_fields[depended[:name]]
39
- event << " #{df.name} = " << (df.reflection.nil? ? "params[:#{df.input_id}]" : "#{df.reflection.class_name}.find_by_id(params[:#{df.input_id}])") << "\n"
40
- # locals[df.name.to_sym] = Code.new(df.name)
41
- end
67
+ def initialize(controller, action_name, model, options={})
68
+ @controller = controller
69
+ @action_name = action_name.to_sym
70
+ @options = (options.is_a?(Hash) ? options : {})
71
+ @model = model
72
+ columns = @options.delete(:columns)
73
+ columns ||= @model.content_columns.collect{|x| x.name.to_sym}.delete_if{|c| [:lock_version, :created_at, :updated_at].include?(c)}
74
+ columns = [columns] unless columns.is_a? Array
75
+ # Normalize columns
76
+ @columns = columns.collect do |c|
77
+ c = c.to_s.split(/\:/) if [String, Symbol].include? c.class
78
+ c = if c.is_a? Hash
79
+ Column.new(@model, c.delete(:name), c)
80
+ elsif c.is_a? Array
81
+ sliced = c[0].split('.')
82
+ Column.new(@model, sliced[-1], :filter=>c[1], :interpolation_key=>c[2], :through=>sliced[0..-2])
83
+ else
84
+ raise Exception.new("Bad column: #{c.inspect}")
85
+ end
86
+ c
87
+ end
88
+ end
89
+
90
+
91
+
92
+ def controller_code()
93
+ foreign_record = @model.name.underscore
94
+ foreign_records = foreign_record.pluralize
95
+ foreign_records = "many_#{foreign_records}" if foreign_record == foreign_records
42
96
 
43
- event << mono_choice_search_code(mono_choice).strip.gsub(/^/, ' ') << "\n"
44
- event << "end\n"
97
+ query = []
98
+ parameters = ''
99
+ initial_conditions = ""
100
+ if @options[:conditions].is_a? Hash
101
+ @options[:conditions].each do |key, value|
102
+ query << (key.is_a?(Symbol) ? @model.table_name+"."+key.to_s : key.to_s)+'=?'
103
+ parameters += ', ' + sanitize_conditions(value)
45
104
  end
46
- code << events.collect{|e| e.gsub(/^/, ' ')}.join
47
- code << " end\n"
48
- end
49
-
50
- # Dependencies refresh
51
- items = @partials
52
- if items.size > 0
53
- code << " if params[:refresh]\n"
54
- code << " #{record_name} = #{form.model.name}.find(params[:id]) if params[:id].to_i > 0\n"
55
- code << " #{record_name} ||= #{form.model.name}.new\n"
56
- code << " @#{record_name} = #{record_name}\n"
57
- events = items.collect do |dependent|
58
- event = "if params[:refresh] == '#{dependent.html_id}'\n"
59
- locals = {record_name.to_sym => Code.new(record_name)}
60
- for depended in dependent.dependeds
61
- df = form.all_fields[depended[:name]]
62
- event << " #{record_name}.#{df.name} = " << (df.reflection.nil? ? "params[:#{df.input_id}]" : "#{df.reflection.class_name}.find_by_id(params[:#{df.input_id}])") << "\n"
63
- # locals[df.name.to_sym] = Code.new(df.name)
64
- end
65
- if dependent.is_a?(Formize::Definition::Field) and dependent.reflection
66
- event << " #{record_name}.#{dependent.reflection.send(Formize.foreign_key)} = params[:selected].to_i if params[:selected]\n"
67
- end
68
- event << " render(:inline=>'<%=#{dependent.prototype}-%>', :locals=>#{locals.inspect})\n"
69
- event << "end\n"
105
+ initial_conditions = query.join(' AND ').inspect+parameters
106
+ elsif @options[:conditions].is_a? Array
107
+ conditions = @options[:conditions]
108
+ if conditions[0].is_a?(String) # SQL
109
+ query << conditions[0].to_s
110
+ parameters += ', '+conditions[1..-1].collect{|p| sanitize_conditions(p)}.join(', ') if conditions.size>1
111
+ else
112
+ raise Exception.new("First element of an Array can only be String or Symbol.")
70
113
  end
71
- code << events.collect{|e| e.gsub(/^/, ' ')}.join
72
- code << " end\n"
114
+ initial_conditions = query.join(' AND ').inspect+parameters
115
+ elsif @options[:conditions].is_a? String
116
+ initial_conditions = "("+@options[:conditions].gsub(/\s*\n\s*/, ';')+")"
117
+ elsif !@options[:conditions].blank?
118
+ raise ArgumentError.new("Option :conditions can be a Hash, an Array or String (not #{@options[:conditions].class}:#{@options[:conditions].inspect})")
73
119
  end
74
120
 
75
- # End
76
- code << "end\n"
77
- code.gsub!(/end\s*if/, 'elsif')
78
- # raise code
79
- # list = code.split("\n"); list.each_index{|x| puts((x+1).to_s.rjust(4)+": "+list[x])}
80
- return code
81
- end
82
-
83
-
84
-
85
- # Generates the view method from a form object
86
- def view_code
87
121
  code = ""
88
-
89
- varh = 'html'
90
-
91
- # Build view methods assimilated to partials
92
- for element in @partials
93
- code << "# #{element.class.name}: #{element.html_id}/#{element.name}\n"
94
- code << view_method_code(element, varh)
95
- end
96
-
97
-
98
- code << "\n"
99
- code << "def #{form.options[:view_fields_method_name]}(#{form.record_name}=nil)\n"
100
- code << " #{form.record_name} = @#{form.record_name} unless #{form.record_name}.is_a?(#{form.model.name})\n"
101
- code << " #{varh} = ''\n"
102
- code << " with_custom_field_error_proc do\n"
103
- for element in form.elements
104
- code << view_method_call(element, varh).strip.gsub(/^/, ' ') << "\n"
122
+ code << "search, conditions = params[:term], []\n"
123
+ code << "if params[:id].to_i > 0\n"
124
+ code << " conditions[0] = '#{@model.table_name}.id = ?'\n"
125
+ code << " conditions << params[:id].to_i\n"
126
+ code << "else\n"
127
+ code << " words = search.to_s.mb_chars.downcase.strip.normalize.split(/[\\s\\,]+/)\n"
128
+ code << " if words.size > 0\n"
129
+ code << " conditions[0] = '('\n"
130
+ code << " words.each_index do |index|\n"
131
+ code << " word = words[index].to_s\n"
132
+ code << " conditions[0] << ') AND (' if index > 0\n"
133
+ if ActiveRecord::Base.connection.adapter_name == "MySQL"
134
+ code << " conditions[0] << "+@columns.collect{|column| "LOWER(CAST(#{column.sql_name} AS CHAR)) LIKE ?"}.join(' OR ').inspect+"\n"
135
+ else
136
+ code << " conditions[0] << "+@columns.collect{|column| "LOWER(CAST(#{column.sql_name} AS VARCHAR)) LIKE ?"}.join(' OR ').inspect+"\n"
105
137
  end
106
- code << " end\n"
107
- code << " return #{varh}.html_safe\n"
108
- code << "end\n"
109
-
110
- code << "\n"
111
- code << "def #{form.options[:view_form_method_name]}(#{form.record_name}=nil)\n"
112
- code << " #{form.record_name} = @#{form.record_name} unless #{form.record_name}.is_a?(#{form.model.name})\n"
113
- code << " #{varh} = ''\n"
114
- code << " if #{form.record_name}.errors.any?\n"
115
- code << " #{varh} << hard_content_tag(:div, :class=>'errors') do |fe|\n"
116
- code << " fe << content_tag(:h2, ::I18n.translate('activerecord.errors.template.body', :count=>#{form.record_name}.errors.size))\n"
117
- code << " if #{form.record_name}.errors[:base].any?\n"
118
- code << " fe << '<ul>'\n"
119
- code << " for error in #{form.record_name}.errors[:base]\n"
120
- code << " fe << content_tag(:li, error)\n"
121
- code << " end\n"
122
- code << " fe << '</ul>'\n"
123
- code << " end\n"
138
+ code << " conditions += ["+@columns.collect{|column| column.filter.inspect.gsub('X', '"+word+"').gsub(/(^\"\"\+|\+\"\"\+|\+\"\")/, '')}.join(", ")+"]\n"
124
139
  code << " end\n"
140
+ code << " conditions[0] << ')'\n"
125
141
  code << " end\n"
126
- code << " #{varh} << form_for(#{form.record_name}, :html=>(params[:dialog] ? {'data-dialog'=>params[:dialog]} : {})) do\n"
127
- code << " concat(#{form.options[:view_fields_method_name]}(#{form.record_name}))\n"
128
- code << " concat(submit_for(#{form.record_name}))\n"
129
- code << " end\n"
130
- code << " return #{varh}.html_safe\n"
131
142
  code << "end\n"
132
143
 
133
- # raise code
134
- # list = code.split("\n"); list.each_index{|x| puts((x+1).to_s.rjust(4)+": "+list[x])}
135
- return code
136
- end
137
-
138
-
139
-
140
-
141
-
142
- def view_partial_code(element, varh='varh')
143
- # send("#{element.class.name.split('::')[-1].underscore}_#{__method__}", element, varh)
144
- special_method = "#{element.class.name.split('::')[-1].underscore}_#{__method__}".to_sym
145
- code = ""
146
- partial_code = send(special_method, element, varh)
147
- dependeds = element.dependeds.collect{|d| d[:name]}
148
- if dependeds.size > 0
149
- for depended in dependeds
150
- depended_field = form.all_fields[depended]
151
- code << "#{depended_field.name} = #{form.record_name}.#{depended_field.name}\n"
152
- if depended_field.reflection
153
- code << "#{depended_field.name} ||= #{field_datasource(depended_field)}.first\n"
154
- end
155
- end
156
-
157
- code << "if #{dependeds.join(' and ')}\n"
158
-
159
- code << partial_code.strip.gsub(/^/, ' ') << "\n"
144
+ joins = (@options[:joins] ? ".joins(#{@options[:joins].inspect}).includes(#{@options[:joins].inspect})" : "")
145
+ order = (@options[:order] ? ".order(#{@options[:order].inspect})" : ".order("+@columns.collect{|c| "#{c.sql_name} ASC"}.join(', ').inspect+")")
146
+ limit = ".limit(#{@options[:limit]||80})"
160
147
 
161
- code << "else\n"
162
- code << " #{varh} << content_tag(:div, '', #{wrapper_attrs(element).inspect})\n"
163
- code << "end\n"
164
- else
165
- code = partial_code
166
- end
167
-
168
- # General condition
169
- if element.options[:if]
170
- code = "if #{options[:if]}\n"+code.strip.gsub(/^/, ' ')+"\nend\n"
171
- elsif element.options[:unless]
172
- code = "unless #{options[:unless]}\n"+code.strip.gsub(/^/, ' ')+"\nend\n"
173
- end
174
- return code
175
- end
148
+ partial = @options[:partial]
176
149
 
177
- def view_method_code(element, varh='varh')
178
- # send("#{element.class.name.split('::')[-1].underscore}_#{__method__}", element, varh)
179
- special_method = "#{element.class.name.split('::')[-1].underscore}_#{__method__}".to_sym
180
- return send(special_method, element, varh) if self.respond_to?(special_method)
181
- code = "def #{element.prototype}\n"
182
- code << " #{varh} = ''\n"
183
- code << view_partial_code(element, varh).strip.gsub(/^/, ' ') << "\n"
184
- code << " return #{varh}.html_safe\n"
185
- code << "end\n"
186
- return code
187
- end
188
-
189
- def view_method_call(element, varh='varh')
190
- special_method = "#{element.class.name.split('::')[-1].underscore}_#{__method__}".to_sym
191
- return send(special_method, element, varh) if self.respond_to?(special_method)
192
- if @partials.include?(element)
193
- return "#{varh} << #{element.prototype}\n"
150
+ html = "<ul><% for #{foreign_record} in #{foreign_records} -%><li id='<%=#{foreign_record}.id-%>'>"
151
+ html << "<% content = item_label_for_#{@action_name}_in_#{@controller.controller_name}(#{foreign_record})-%>"
152
+ if partial
153
+ html << "<%=render(:partial=>#{partial.inspect}, :locals =>{:#{foreign_record}=>#{foreign_record}, :content=>content, :search=>search})-%>"
194
154
  else
195
- return view_partial_code(element, varh).strip << "\n"
196
- end
197
- end
198
-
199
- def wrapper_attrs(element)
200
- html_options = (element.respond_to?(:html_options) ? element.html_options.dup : {})
201
- html_options[:id] = element.html_id
202
- if element.options[:hidden_if]
203
- field = form.all_fields[element.options[:hidden_if]]
204
- raise ArgumentError.new("Option :hidden_if must reference a check_box field (Not #{field.type.inspect})") unless field.type == :check_box
205
- html_options['data-hidden-if'] = field.input_id
206
- elsif element.options[:shown_if]
207
- field = form.all_fields[element.options[:shown_if]]
208
- raise ArgumentError.new("Option :shown_if must reference a check_box field (Not #{field.type.inspect})") unless field.type == :check_box
209
- html_options['data-shown-if'] = field.input_id
210
- end
211
- if @partials.include?(element)
212
- url = {:controller => controller.controller_name.to_sym, :action=>form.action_name.to_sym, :refresh=>element.html_id}
213
- # for depended in element.dependeds
214
- # df = form.all_fields[depended[:name]]
215
- # # url[df.input_id.to_sym] = Code.new(df.reflection.nil? ? df.name : "#{df.name}.id")
216
- # end
217
- html_options["data-refresh"] = Code.new("url_for(#{url.inspect})")
155
+ html << "<%=highlight(content, search)-%>"
218
156
  end
219
- special_method = "#{element.class.name.split('::')[-1].underscore}_#{__method__}".to_sym
220
- return send(special_method, element, html_options) if self.respond_to?(special_method)
221
- return html_options
222
- end
223
-
224
- #####################################################################################
225
- # F I E L D _ S E T M E T H O D S #
226
- #####################################################################################
227
-
228
- def group_view_partial_code(group, varh='varh')
229
- # Initialize html attributes
230
- html_options = wrapper_attrs(group)
157
+ html << '</li><%end-%></ul>'
231
158
 
232
- varc = group.html_id # "group_#{group.html_id}"
233
- code = "#{varh} << hard_content_tag(:div, #{html_options.inspect}) do |#{varc}|\n"
234
- for child in group.children
235
- code << view_method_call(child, varc).strip.gsub(/^/, ' ') << "\n"
236
- end
159
+ code << "#{foreign_records} = #{@model.name}"
160
+ code << ".where(#{initial_conditions})" unless initial_conditions.blank?
161
+ code << ".where(conditions)"+joins+order+limit+"\n"
162
+ # Render HTML is old Style
163
+ code << "respond_to do |format|\n"
164
+ code << " format.html { render :inline=>#{html.inspect}, :locals=>{:#{foreign_records}=>#{foreign_records}, :search=>search} }\n"
165
+ code << " format.json { render :json=>#{foreign_records}.collect{|#{foreign_record}| {:label=>#{item_label(foreign_record)}, :id=>#{foreign_record}.id}}.to_json }\n"
166
+ code << " format.yaml { render :yaml=>#{foreign_records}.collect{|#{foreign_record}| {'label'=>#{item_label(foreign_record)}, 'id'=>#{foreign_record}.id}}.to_yaml }\n"
167
+ code << " format.xml { render :xml=>#{foreign_records}.collect{|#{foreign_record}| {:label=>#{item_label(foreign_record)}, :id=>#{foreign_record}.id}}.to_xml }\n"
237
168
  code << "end\n"
238
169
  return code
239
170
  end
240
-
241
171
 
242
- def field_set_view_partial_code(field_set, varh='varh')
243
- # Initialize html attributes
244
- html_options = wrapper_attrs(field_set)
245
172
 
246
- varc = field_set.html_id # "field_set_#{field_set.html_id}"
247
- code = "#{varh} << hard_content_tag(:fieldset, #{html_options.inspect}) do |#{varc}|\n"
248
- unless field_set.title.nil?
249
- code << " #{varc} << content_tag(:legend, ::I18n.translate('labels.#{field_set.title}'))\n"
250
- end
251
- for child in field_set.children
252
- code << view_method_call(child, varc).strip.gsub(/^/, ' ') << "\n"
253
- end
173
+ def controller_action()
174
+ code = "def #{@action_name}\n"
175
+ code << self.controller_code.strip.gsub(/^/, ' ')+"\n"
254
176
  code << "end\n"
177
+ # list = code.split("\n"); list.each_index{|x| puts((x+1).to_s.rjust(4)+": "+list[x])}
255
178
  return code
256
179
  end
257
180
 
258
- #####################################################################################
259
- # F I E L D M E T H O D S #
260
- #####################################################################################
261
-
262
- def field_view_partial_code(field, varh='varh')
263
- input_attrs = (field.options[:input_options].is_a?(Hash) ? field.options[:input_options] : {})
264
- # deps = form.dependents_on(field)
265
- # if deps.size > 0
266
- # input_attrs["data-dependents"] = deps.collect{|d| "##{d.html_id}"}.join(',')
267
- # end
268
- for method, name in {:dependents_on=>"dependents", :shown_if=>"show", :hidden_if=>"hide"}
269
- elements = form.send(method, field)
270
- if elements.size > 0
271
- input_attrs["data-#{name}"] = elements.collect{|d| "##{d.html_id}"}.join(',')
272
- end
273
- end
274
-
275
- # Initialize html attributes
276
- html_options = wrapper_attrs(field)
277
181
 
278
- varc = field.html_id
279
- code = "#{varh} << hard_content_tag(:table, #{html_options.inspect}) do |#{varc}|\n"
280
- code << " #{varc} << '<tr><td class=\"label\">'\n"
281
- code << " #{varc} << label(:#{form.record_name}, :#{field.name}, nil, :class=>'attr')\n"
282
- code << " #{varc} << '</td><td class=\"input\">'\n"
283
- code << " #{form.record_name}.#{field.name} ||= (#{Code.new(field.default)})\n" if field.default
284
- code << self.send("field_#{field.type}_input", field, input_attrs, varc).strip.gsub(/^/, ' ') << "\n"
285
- code << " if @#{form.record_name}.errors['#{field.name}'].any?\n"
286
- code << " #{varc} << '<ul class=\"inline-errors\">'\n"
287
- code << " for error in @#{form.record_name}.errors['#{field.name}']\n"
288
- code << " #{varc} << content_tag(:li, error)\n"
289
- code << " end\n"
290
- code << " #{varc} << '</ul>'\n"
182
+ def item_label_code()
183
+ record = 'record'
184
+ code = "def self.item_label_for_#{@action_name}_in_#{@controller.controller_name}(#{record})\n"
185
+ code << " if #{record}.is_a? #{@model.name}\n"
186
+ code << " return #{item_label(record)}\n"
187
+ code << " else\n"
188
+ code << " return ''\n"
291
189
  code << " end\n"
292
- code << " #{varc} << '</td></tr>'\n"
293
- code << "end\n"
294
- return code
295
- end
296
-
297
- def field_datasource(field)
298
- source = field.source
299
- source = Formize.default_source unless [Array, String, Symbol].include?(source.class)
300
- if source.is_a?(Array)
301
- return "#{source[0]}.#{field.choices}"
302
- elsif source == :foreign_class
303
- # return field.reflection.class_name if field.choices.to_s == "all"
304
- return "#{field.reflection.class_name}.#{field.choices}"
305
- elsif source == :class
306
- # return form.model.name if field.choices.to_s == "all"
307
- return "#{form.model.name}.#{field.choices}"
308
- else
309
- return "#{source}.#{field.choices}"
310
- end
311
- end
312
-
313
- # Returns the name of the class of the source
314
- #
315
- def field_datasource_class_name(field)
316
- source = field.source
317
- source = Formize.default_source unless [Array, String, Symbol].include?(source.class)
318
- if source.is_a?(Array)
319
- return source[1]
320
- elsif source == :foreign_class
321
- return field.reflection.class_name
322
- elsif source == :class
323
- return form.model.name
324
- else
325
- return source.to_s.classify
326
- end
327
- end
328
-
329
- def field_input_options(field)
330
- end
331
-
332
- def field_wrapper_attrs(field, html_options={})
333
- html_options[:class] = "fz field #{html_options[:class]}".strip
334
- html_options[:class] = "#{html_options[:class]} #{field.type.to_s.gsub('_', '-')}".strip
335
- html_options[:class] = "#{html_options[:class]} required".strip if field.required
336
- html_options[:class] = Code.new("\"#{html_options[:class]}\#{' invalid' if @#{field.form.record_name}.errors['#{field.name}'].any?}\"")
337
- return html_options
338
- end
339
-
340
-
341
-
342
-
343
- def field_check_box_input(field, attrs={}, varc='varc')
344
- return "#{varc} << check_box(:#{field.record_name}, :#{field.method}, #{attrs.inspect})\n"
345
- end
346
-
347
- def field_choice_input(field, attrs={}, varc='varc')
348
- code = if field.choices.size <= Formize.radio_count_max
349
- field_radio_input(field, attrs, varc)
350
- else
351
- field_select_input(field, attrs, varc)
352
- end
353
- return code
354
- end
355
-
356
- def field_date_input(field, attrs={}, varc='varc')
357
- attrs[:size] ||= 10
358
- return "#{varc} << date_field(:#{field.record_name}, :#{field.method}, #{attrs.inspect})\n"
359
- end
360
-
361
- def field_datetime_input(field, attrs={}, varc='varc')
362
- attrs[:size] ||= 16
363
- # TODO define Date format
364
- return "#{varc} << datetime_field(:#{field.record_name}, :#{field.method}, #{attrs.inspect})\n"
365
- end
366
-
367
- def field_label_input(field, attrs={}, varc='varc')
368
- attrs[:class] = (attrs[:class]+" readonly").strip
369
- return "#{varc} << content_tag(:span, @:#{field.record_name}.#{field.method}, #{attrs.inspect})\n"
370
- end
371
-
372
- def field_numeric_input(field, attrs={}, varc='varc')
373
- attrs[:size] ||= 16
374
- return "#{varc} << text_field(:#{field.record_name}, :#{field.method}, #{attrs.inspect})\n"
375
- end
376
-
377
- def field_password_input(field, attrs={}, varc='varc')
378
- attrs[:size] ||= 24
379
- return "#{varc} << password_field(:#{field.record_name}, :#{field.method}, #{attrs.inspect})\n"
380
- end
381
-
382
- def field_radio_input(field, attrs={}, varc='varc')
383
- return "#{varc} << radio(:#{field.record_name}, :#{field.method}, #{field.choices.inspect}, #{attrs.inspect})\n"
384
- # return "#{varc} << " << field.choices.collect{|x| "content_tag(:span, radio_button(:#{field.record_name}, :#{field.method}, #{x[1].inspect}) << ' ' << content_tag(:label, #{x[0].inspect}, :for=>'#{field.input_id}_#{x[1]}'), :class=>'rad')"}.join(" << ") << "\n"
385
- end
386
-
387
- def field_select_input(field, attrs={}, varc='varc')
388
- if (include_blank = attrs.delete(:include_blank)).is_a? String
389
- field.choices.insert(0, [include_blank, ''])
390
- end
391
- return "#{varc} << select(:#{field.record_name}, :#{field.method}), #{field.choices.inspect}, #{attrs.inspect})\n"
392
- end
393
-
394
- def field_mono_choice_input(field, attrs={}, varc='varc')
395
- source_model = field_datasource_class_name(field).constantize
396
- reflection = source_model.reflections[field.choices]
397
- # if reflection.nil?
398
- # raise Exception.new("#{source_model.name} must have a reflection :#{field.choices}.")
399
- # end
400
- count = "#{field.choices}_count"
401
- select_first_if_empty = " #{record_name}.#{field.name} ||= #{field_datasource(field)}.first\n"
402
- code = "#{count} = #{field_datasource(field)}.count\n"
403
- code << "if (#{count} == 0)\n"
404
- code << field_mono_select_input(field, attrs, varc).strip.gsub(/^/, ' ') << "\n"
405
- code << "elsif (#{count} <= #{Formize.radio_count_max})\n"
406
- code << select_first_if_empty
407
- code << field_mono_radio_input(field, attrs, varc).strip.gsub(/^/, ' ') << "\n"
408
- if !reflection or (reflection and reflection.options[:finder_sql].nil?)
409
- code << "elsif (#{count} <= #{Formize.select_count_max})\n"
410
- code << select_first_if_empty
411
- code << field_mono_select_input(field, attrs, varc).strip.gsub(/^/, ' ') << "\n"
412
- code << "else\n"
413
- code << select_first_if_empty
414
- code << field_mono_unroll_input(field, attrs, varc).strip.gsub(/^/, ' ') << "\n"
415
- else
416
- code << "else\n"
417
- code << select_first_if_empty
418
- code << field_mono_select_input(field, attrs, varc).strip.gsub(/^/, ' ') << "\n"
419
- end
420
190
  code << "end\n"
421
-
422
- new_item_url = field.options.delete(:new)
423
- if new_item_url.is_a? Symbol
424
- new_item_url = {:controller=>new_item_url.to_s.pluralize.to_sym}
425
- elsif new_item_url.is_a? TrueClass
426
- new_item_url = {}
427
- end
428
-
429
- if new_item_url.is_a?(Hash)
430
- for k, v in new_item_url
431
- new_item_url[k] = Code.new(v) if v.is_a?(String)
432
- end
433
- edit_item_url = {} unless edit_item_url.is_a? Hash
434
- if field.method.to_s.match(/_id$/) and refl = field.reflection # form.model.reflections[field.method.to_s[0..-4].to_sym]
435
- new_item_url[:controller] ||= refl.class_name.underscore.pluralize
436
- edit_item_url[:controller] ||= new_item_url[:controller]
437
- end
438
- new_item_url[:action] ||= :new
439
- edit_item_url[:action] ||= :edit
440
- data = field.options.delete(:update)||field.html_id
441
- html_options = {"data-add-item"=>data, :class=>"icon im-new"}
442
- code << "#{varc} << content_tag(:span, content_tag(:span, link_to(::I18n.translate('form.new'), #{new_item_url.inspect}, #{html_options.inspect}).html_safe, :class=>:tool).html_safe, :class=>\"toolbar mini-toolbar\")\n" #  if authorized?(#{new_item_url.inspect})
443
- end
191
+ # puts code
444
192
  return code
445
193
  end
446
194
 
447
- def field_mono_radio_input(field, attrs={}, varc='varc')
448
- return "#{varc} << radio(:#{field.record_name}, :#{field.method}, #{field_datasource(field)}.collect{|item| [item.#{field.item_label}, item.id]}, {}, #{attrs.inspect})"
449
- end
195
+ private
450
196
 
451
- def field_mono_select_input(field, attrs={}, varc='varc')
452
- return "#{varc} << select(:#{field.record_name}, :#{field.method}, #{field_datasource(field)}.collect{|item| [item.#{field.item_label}, item.id]}, {}, #{attrs.inspect})"
197
+ def item_label(record, options={})
198
+ return "::I18n.translate('views.unroll.#{@controller.controller_name}.#{@action_name}', "+@columns.collect{|c| ":#{c.interpolation_key}=>#{c.value_code(record)}"}.join(', ')+", :default=>[:'labels.#{@action_name}', '"+@columns.collect{|c| "%{#{c.interpolation_key}}"}.join(', ')+"']).gsub(/%s/, \"\\u{00A0}\")"
453
199
  end
454
-
455
- def field_mono_unroll_input(field, attrs={}, varc='varc')
456
- options = {}
457
- options[:label] ||= Code.new("Proc.new{|r| \"#{mono_choice_label(field, 'r')}\"}")
458
- url = {:controller=>controller.controller_name, :action=>form.action_name, :unroll=>field.html_id}
459
- for depended in field.dependeds
460
- df = form.all_fields[depended[:name]]
461
- url[df.input_id.to_sym] = Code.new(df.reflection.nil? ? df.name : "#{df.name}.id")
462
- end
463
- return "#{varc} << unroll(:#{field.record_name}, :#{field.method}, #{url.inspect}, #{options.inspect}, #{attrs.inspect})"
464
- end
465
-
466
- def field_string_input(field, attrs={}, varc='varc')
467
- attrs[:size] ||= 24
468
- if field.column and !field.column.limit.nil?
469
- attrs[:size] = field.column.limit if field.column.limit<attrs[:size]
470
- attrs[:maxlength] = field.column.limit
471
- end
472
- return "#{varc} << text_field(:#{field.record_name}, :#{field.method}, #{attrs.inspect})\n"
473
- end
474
-
475
- def field_text_area_input(field, attrs={}, varc='varc')
476
- attrs[:cols] ||= 40
477
- attrs[:rows] ||= 3
478
- attrs[:class] = "#{attrs[:class]} #{attrs[:cols]==80 ? :code : nil}".strip
479
- return "#{varc} << resizable_text_area(:#{field.record_name}, :#{field.method}, #{attrs.inspect})\n"
480
- end
481
-
482
-
483
-
484
-
485
-
486
-
487
- protected
488
200
 
489
201
  def sanitize_conditions(value)
490
202
  if value.is_a? Array
491
- if value.size==1 and value[0].is_a? String
203
+ if value.size == 1 and value[0].is_a? String
492
204
  value[0].to_s
493
205
  else
494
206
  value.inspect
@@ -502,107 +214,8 @@ module Formize
502
214
  end
503
215
  end
504
216
 
505
-
506
- def mono_choice_label(choice, varr='record')
507
- return "\#\{#{varr}.#{choice.item_label}\}"
508
- end
509
-
510
- def mono_choice_search_code(field)
511
- source_model = field_datasource_class_name(field).constantize
512
- # reflection = source_model.reflections[field.choices]
513
- # if reflection.nil?
514
- # raise Exception.new("#{source_model.name} must have a reflection :#{field.choices}.")
515
- # end
516
- model = field.reflection.class_name.constantize #reflection.class_name.constantize
517
- foreign_record = model.name.underscore
518
- foreign_records = "#{source_model.name.underscore}_#{field.choices}"
519
- options = field.options
520
- attributes = field.search_attributes
521
- attributes = [attributes] unless attributes.is_a? Array
522
- attributes_hash = {}
523
- attributes.each_index do |i|
524
- attribute = attributes[i]
525
- attributes[i] = [
526
- (attribute.to_s.match(/\./) ? attribute.to_s : model.table_name+'.'+attribute.to_s.split(/\:/)[0]),
527
- (attribute.to_s.match(/\:/) ? attribute.to_s.split(/\:/)[1] : (options[:filter]||'%X%')),
528
- '_a'+i.to_s]
529
- attributes_hash[attributes[i][2]] = attributes[i][0]
530
- end
531
- query = []
532
- parameters = ''
533
- if options[:conditions].is_a? Hash
534
- options[:conditions].each do |key, value|
535
- query << (key.is_a?(Symbol) ? model.table_name+"."+key.to_s : key.to_s)+'=?'
536
- parameters += ', ' + sanitize_conditions(value)
537
- end
538
- elsif options[:conditions].is_a? Array
539
- conditions = options[:conditions]
540
- case conditions[0]
541
- when String # SQL
542
- # query << '["'+conditions[0].to_s+'"'
543
- query << conditions[0].to_s
544
- parameters += ', '+conditions[1..-1].collect{|p| sanitize_conditions(p)}.join(', ') if conditions.size>1
545
- # query << ')'
546
- else
547
- raise Exception.new("First element of an Array can only be String or Symbol.")
548
- end
549
- end
550
-
551
- select = (model.table_name+".id AS id, "+attributes_hash.collect{|k,v| v+" AS "+k}.join(", ")).inspect
552
-
553
- code = ""
554
- code << "conditions = [#{query.join(' AND ').inspect+parameters}]\n"
555
- code << "search = params[:term]\n"
556
- code << "words = search.to_s.mb_chars.downcase.strip.normalize.split(/[\\s\\,]+/)\n"
557
- code << "if words.size > 0\n"
558
- code << " conditions[0] << '#{' AND ' if query.size>0}('\n"
559
- code << " words.each_index do |index|\n"
560
- code << " word = words[index].to_s\n"
561
- code << " conditions[0] << ') AND (' if index > 0\n"
562
-
563
- if ActiveRecord::Base.connection.adapter_name == "MySQL"
564
- code << " conditions[0] << "+attributes.collect{|key| "LOWER(CAST(#{key[0]} AS CHAR)) LIKE ?"}.join(' OR ').inspect+"\n"
565
- else
566
- code << " conditions[0] << "+attributes.collect{|key| "LOWER(CAST(#{key[0]} AS VARCHAR)) LIKE ?"}.join(' OR ').inspect+"\n"
567
- end
568
-
569
- code << " conditions += ["+attributes.collect{|key| key[1].inspect.gsub('X', '"+word+"').gsub(/(^\"\"\+|\+\"\"\+|\+\"\")/, '')}.join(", ")+"]\n"
570
- code << " end\n"
571
- code << " conditions[0] << ')'\n"
572
- code << "end\n"
573
-
574
- # joins = options[:joins] ? ", :joins=>"+options[:joins].inspect : ""
575
- # order = ", :order=>"+attributes.collect{|key| "#{key[0]} ASC"}.join(', ').inspect
576
- # limit = ", :limit=>"+(options[:limit]||80).to_s
577
- joins = options[:joins] ? ".joins(#{options[:joins].inspect})" : ""
578
- order = ".order("+attributes.collect{|key| "#{key[0]} ASC"}.join(', ').inspect+")"
579
- limit = ".limit(#{options[:limit]||80})"
580
-
581
- partial = options[:partial]
582
-
583
- html = "<ul><%for #{foreign_record} in #{foreign_records}-%><li id='<%=#{foreign_record}.id-%>'>"
584
- html << "<%content=#{foreign_record}.#{field.item_label}-%>"
585
- # html << "<%content="+attributes.collect{|key| "#{foreign_record}['#{key[2]}'].to_s"}.join('+", "+')+" -%>"
586
- if partial
587
- html << "<%=render(:partial=>#{partial.inspect}, :locals =>{:#{foreign_record}=>#{foreign_record}, :content=>content, :search=>search})-%>"
588
- else
589
- html << "<%=highlight(content, search)-%>"
590
- end
591
- html << '</li><%end-%></ul>'
592
-
593
- # code << "#{foreign_records} = #{field_datasource(field)}.find(:all, :conditions=>conditions"+joins+order+limit+")\n"
594
- code << "#{foreign_records} = #{field_datasource(field).gsub(/\.all$/, '')}.where(conditions)"+joins+order+limit+"\n"
595
- # Render HTML is old Style
596
- code << "respond_to do |format|\n"
597
- code << " format.html { render :inline=>#{html.inspect}, :locals=>{:#{foreign_records}=>#{foreign_records}, :search=>search} }\n"
598
- code << " format.json { render :json=>#{foreign_records}.collect{|#{foreign_record}| {:label=>#{foreign_record}.#{field.item_label}, :id=>#{foreign_record}.id}}.to_json }\n"
599
- code << " format.xml { render :xml=>#{foreign_records}.collect{|#{foreign_record}| {:label=>#{foreign_record}.#{field.item_label}, :id=>#{foreign_record}.id}}.to_xml }\n"
600
- code << "end\n"
601
- return code
602
- end
217
+ end
603
218
 
219
+ end
604
220
 
605
-
606
- end
607
- end
608
221
  end