formize 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,56 @@
1
+ module Formize
2
+
3
+ # Main class for Form definitions
4
+ # It permits to manage tree of form elements
5
+ class Element
6
+ attr_reader :parent, :children, :method_name, :id
7
+ @@count = 0
8
+
9
+ def initialize(parent = nil, is_method = false)
10
+ @parent = parent
11
+ @children = []
12
+ @is_method = is_method
13
+ @@count += 1
14
+ @id = @@count.to_s(36)
15
+ @method_name = "_formize_#{@id}"
16
+ end
17
+
18
+ def is_method?
19
+ @is_method
20
+ end
21
+
22
+ def is_method!(value = true)
23
+ raise ArgumentError.new("Must be true or false (not #{value.inspect})") unless [TrueClass, FalseClass].include?(value.class)
24
+ @is_method = value
25
+ end
26
+
27
+ def method_name=(name)
28
+ raise ArgumentError.new("Name of field_set must be written only with a-z and 0-9 and _ (not #{name.inspect})") unless name.to_s == name.to_s.downcase.gsub(/[^a-z0-9\_]/, '')
29
+ @method_name = name
30
+ end
31
+
32
+
33
+ def method_code(options={})
34
+ varh = options[:html_variable] ||= 'html'
35
+ code = "def #{method_name}(record)\n"
36
+ code << inner_method_code(options).gsub(/^/, ' ')
37
+ code << " return #{varh}\n"
38
+ code << "end\n"
39
+ return code
40
+ end
41
+
42
+ def method_call_code(options={})
43
+ return inner_method_code(options) unless self.is_method?
44
+ return "#{method_name}(record)"
45
+ end
46
+
47
+ def inner_method_code(options={})
48
+ # raise NotImplementedError.new
49
+ return content_tag(:strong, "'#{self.class.name} does not implement :#{__method__} method'", options)
50
+ end
51
+
52
+
53
+ end
54
+
55
+
56
+ end
@@ -0,0 +1,67 @@
1
+ module Formize
2
+
3
+
4
+ # Represents the field element
5
+ class Field < FormElement
6
+ attr_reader :name, :options, :column, :record_name, :method, :type, :required, :choices, :input_id, :source, :item_label, :field_id, :reflection, :html_options, :default, :search_attributes
7
+
8
+ TYPES = [:check_box, :choice, :date, :datetime, :label, :numeric, :password, :mono_choice, :string, :text_area].freeze
9
+
10
+ def initialize(form, parent, name, options={})
11
+ super(form, parent)
12
+ @name = name.to_s
13
+ @options = (options.is_a?(Hash) ? options : {})
14
+ @column = form.model.columns_hash[@name]
15
+ @record_name = form.record_name
16
+ @method = @name
17
+ unless @options[:default].nil?
18
+ @default = (@options[:default].is_a?(String) ? Code.new(@options[:default]) : @options[:default])
19
+ end
20
+ @html_options = @options.delete(:html_options)||{}
21
+ @depend_on = @options.delete(:depend_on)
22
+ raise ArgumentError.new("A depended element must defined before its dependencies (#{@depended.inspect})") if !@depend_on.blank? and form.fields[@depend_on].nil?
23
+ if type = @options.delete(:as)
24
+ raise ArgumentError.new("Unknown field type (got #{@options[:as].inspect}, expects #{TYPES.join(', ')})") unless TYPES.include? type
25
+ @type = type
26
+ else
27
+ @type = :password if @name.to_s.match /password/
28
+ if @choices = @options.delete(:choices)
29
+ if @choices.is_a? Array
30
+ @type = :choice
31
+ elsif [Symbol, Hash].include? @choices.class
32
+ @type = :mono_choice
33
+ @reflection = form.model.reflections[@method.to_sym]
34
+ @source = @options.delete(:source) # || @reflection.class_name
35
+ @is_method = true if @options[:new]
36
+ @method_name = self.form.unique_name + "_inf_" + @name
37
+ @method = @reflection.primary_key_name
38
+ unless @item_label = @options.delete(:item_label)
39
+ model = @reflection.class_name.constantize
40
+ available_methods = (model.columns_hash.keys+model.instance_methods).collect{|x| x.to_s}
41
+ @item_label = [:label, :name, :code, :number, :inspect].detect{|x| available_methods.include?(x.to_s)}
42
+ end
43
+ @search_attributes = @options[:search] || @reflection.class_name.constantize.content_columns.select{|c| c.type != :boolean and ![:created_at, :updated_at, :lock_version].include?(c.name.to_sym)}.collect{|c| c.name.to_sym}
44
+ else
45
+ raise ArgumentError.new("Option :choices must be Array, Symbol or Hash (got #{@choices.class.name})")
46
+ end
47
+ end
48
+ if column
49
+ @type = :check_box if column.type == :boolean
50
+ @type = :date if column.type == :date
51
+ @type = :datetime if column.type==:datetime or column.type==:timestamp
52
+ @type = :numeric if [:integer, :float, :decimal].include? column.type
53
+ @type = :text_area if column.type == :text
54
+ end
55
+ @type = :label if @form.model.readonly_attributes.include? @record_name
56
+ @type ||= :string
57
+ end
58
+ @required = false
59
+ @required = !@column.null if @column
60
+ @required = true if @options.delete(:required).is_a?(TrueClass)
61
+ @input_id = form.model.name.underscore << '_' << method.to_s
62
+ @field_id = "ff" << Time.now.to_i.to_s(36) << rand.to_s[2..-1].to_i.to_s(36)
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,40 @@
1
+ module Formize
2
+
3
+
4
+
5
+ # Represents a group of fields which can depend on other fields
6
+ class FieldSet < FormElement
7
+ attr_reader :name, :options, :title, :html_options
8
+
9
+ def initialize(form, parent, name=nil, options={})
10
+ super(form, parent)
11
+ @title = nil
12
+ @name = if name.blank?
13
+ rand.to_s[2..-1].to_i.to_s(36)
14
+ else
15
+ raise ArgumentError.new("Name of field_set must be written only with a-z and 0-9 and _ (not #{name.inspect})") unless name.to_s == name.to_s.downcase.gsub(/[^a-z0-9\_]/, '')
16
+ @title = name
17
+ name.to_s
18
+ end
19
+ @depend_on = options.delete(:depend_on)
20
+ raise ArgumentError.new("A depended element must defined before its dependencies (#{@depended.inspect})") if !@depend_on.blank? and form.fields[@depend_on].nil?
21
+ @options = (options.is_a?(Hash) ? options : {})
22
+ @html_options = @options.delete(:html_options)||{}
23
+ end
24
+
25
+
26
+ def field_set(name=nil, options={}, &block)
27
+ raise ArgumentError.new("Missing block") unless block_given?
28
+ field_set = self.new_child(FieldSet, name, options)
29
+ yield field_set
30
+ end
31
+
32
+ def field(name, options={})
33
+ self.new_child(Field, name, options)
34
+ end
35
+
36
+ end
37
+
38
+
39
+
40
+ end
@@ -0,0 +1,89 @@
1
+ module Formize
2
+
3
+ # Represents an environment for a form or list of fields of one Record
4
+ class Form
5
+ attr_reader :model, :elements, :record_name, :unique_name, :options, :id
6
+ @@count = 0
7
+
8
+
9
+ def initialize(name, model, options={})
10
+ @name = name
11
+ @model = model
12
+ @options = options
13
+ @elements = []
14
+ @@count += 1
15
+ @id = @@count.to_s(36)
16
+ @unique_name = @options.delete(:unique_name) unless @options[:unique_name].blank?
17
+ @unique_name ||= "_formize#{@id}"
18
+ @record_name = @model.name.underscore
19
+ end
20
+
21
+ def field_set(name=nil, options={}, &block)
22
+ raise ArgumentError.new("Missing block") unless block_given?
23
+ field_set = new_element(FieldSet, name, options)
24
+ yield field_set
25
+ end
26
+
27
+ def field(name, options={})
28
+ return new_element(Field, name, options)
29
+ end
30
+
31
+ # def inner_method_code(options={})
32
+ # varh = options[:html_variable] || 'html'
33
+ # code = "#{varh} = ''\n"
34
+ # for child in children
35
+ # code << "#{varh} << " << child.method_call_code << "\n"
36
+ # end
37
+ # code << "return #{varh}\n"
38
+ # return code
39
+ # end
40
+
41
+ def controller_method_name
42
+ @options[:controller_method_name] || "formize_#{model.underscore}"
43
+ end
44
+
45
+ def view_method_name
46
+ @options[:method_name] || "_form_#{model.underscore}"
47
+ end
48
+
49
+ def action_name
50
+ @options[:action_name] || :formize
51
+ end
52
+
53
+ # def methodics
54
+ # return elements.collect{|e| e.methodics}.flatten
55
+ # end
56
+
57
+ def mono_choices
58
+ return elements.collect{|e| e.mono_choices}.flatten
59
+ end
60
+
61
+ def fields
62
+ return elements.inject(HashWithIndifferentAccess.new){|h, e| h.merge!(e.fields)}
63
+ end
64
+
65
+ def dependents
66
+ return elements.collect{|e| e.dependents}.flatten
67
+ end
68
+
69
+ def all_elements
70
+ return elements.collect{|e| e.all_elements}.flatten
71
+ end
72
+
73
+ def dependents_on(element)
74
+ return elements.collect{|e| e.dependents_on(element)}.flatten
75
+ end
76
+
77
+
78
+ private
79
+
80
+ def new_element(klass, *args)
81
+ raise ArgumentError.new("Bad child type (#{klass.name}). Must be an Formize::FormElement") unless klass < FormElement
82
+ element = klass.new(self, nil, *args)
83
+ @elements << element
84
+ return element
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,140 @@
1
+ module Formize
2
+
3
+ # Main class for form elements
4
+ class FormElement
5
+ attr_reader :form, :parent, :children, :unique_name, :id, :depend_on, :html_id
6
+ @@count = 0
7
+
8
+ def initialize(form, parent = nil)
9
+ raise ArgumentError.new("Bad form (#{form.class.name}). Must be an Formize::Form") unless form.is_a? Formize::Form
10
+ @form = form
11
+ @parent = parent
12
+ @depend_on = nil
13
+ @children = []
14
+ @@count += 1
15
+ @id = @@count.to_s(36)
16
+ @html_id = "fz#{@id}"
17
+ @unique_name = self.form.unique_name + "_" + @html_id
18
+ end
19
+
20
+
21
+ def dependeds
22
+ l = (self.parent ? self.parent.dependeds : [])
23
+ l << {:name=>self.depend_on} unless self.depend_on.blank?
24
+ return l
25
+ end
26
+
27
+ def arguments
28
+ args = []
29
+ args << {:name=>form.record_name}
30
+ # args += self.dependeds
31
+ # args << {:name=>@depend_on} if @depend_on
32
+ return args
33
+ end
34
+
35
+ def prototype
36
+ return "#{@unique_name}(" + arguments.collect{|x| x[:name]}.join(', ') + ")"
37
+ end
38
+
39
+ # def method_name=(name)
40
+ # raise ArgumentError.new("Name of field_set must be written only with a-z and 0-9 and _ (not #{name.inspect})") unless name.to_s == name.to_s.downcase.gsub(/[^a-z0-9\_]/, '')
41
+ # @method_name = name
42
+ # end
43
+
44
+
45
+ # def method_code(options={})
46
+ # varh = options[:html_variable] ||= 'html'
47
+ # code = "def #{method_name}(record)\n"
48
+ # code << inner_method_code(options).gsub(/^/, ' ')
49
+ # code << " return #{varh}\n"
50
+ # code << "end\n"
51
+ # return code
52
+ # end
53
+
54
+ # def method_call_code(options={})
55
+ # return inner_method_code(options) unless self.is_method?
56
+ # return "#{method_name}(record)"
57
+ # end
58
+
59
+ # def inner_method_code(options={})
60
+ # # raise NotImplementedError.new
61
+ # return content_tag(:strong, "'#{self.class.name} does not implement :#{__method__} method'", options)
62
+ # end
63
+
64
+
65
+
66
+ # def is_method?
67
+ # @depend_on.nil?
68
+ # end
69
+
70
+ # def methodics
71
+ # elements = []
72
+ # for child in self.children
73
+ # elements += child.methodics
74
+ # end
75
+ # elements << self if self.is_method?
76
+ # return elements
77
+ # end
78
+
79
+ def mono_choices
80
+ elements = []
81
+ for child in self.children
82
+ elements += child.mono_choices
83
+ end
84
+ elements << self if self.class == Formize::Field and self.type == :mono_choice
85
+ return elements
86
+ end
87
+
88
+ def fields
89
+ elements = HashWithIndifferentAccess.new()
90
+ for child in self.children
91
+ elements.merge!(child.fields)
92
+ end
93
+ elements[self.name] = self if self.class == Formize::Field
94
+ return elements
95
+ end
96
+
97
+ def dependents
98
+ elements = []
99
+ for child in self.children
100
+ elements += child.dependents
101
+ end
102
+ elements << self if self.options[:depend_on]
103
+ return elements
104
+ end
105
+
106
+
107
+ def all_elements
108
+ elements = self.children.collect{|c| c.all_elements}.flatten
109
+ elements << self
110
+ return elements
111
+ end
112
+
113
+
114
+ # Find form elements
115
+ def dependents_on(element)
116
+ elements = []
117
+ for child in self.children
118
+ elements += child.dependents_on(element)
119
+ end
120
+ elements << self if self.depend_on and self.depend_on.to_s == element.name.to_s # form.fields[self.depend_on].name == element.name
121
+ return elements
122
+ end
123
+
124
+
125
+
126
+
127
+ protected
128
+
129
+ def new_child(klass, *args)
130
+ raise ArgumentError.new("Bad child type (#{klass.name}). Must be an Formize::FormElement") unless klass < FormElement
131
+ element = klass.new(self.form, self, *args)
132
+ @children << element
133
+ return element
134
+ end
135
+
136
+
137
+ end
138
+
139
+
140
+ end
@@ -0,0 +1,5 @@
1
+ # require 'formize/definition/element'
2
+ require 'formize/definition/form'
3
+ require 'formize/definition/form_element'
4
+ require 'formize/definition/field_set'
5
+ require 'formize/definition/field'
@@ -0,0 +1,62 @@
1
+ module Formize
2
+ module FormHelper
3
+
4
+ # Generates a form with all its fields as defined in controller.
5
+ # If no name is given, it uses the name of the controller to find the corresponding model
6
+ def formize_form(*args)
7
+ name, options = nil, {}
8
+ name = args[0] if args[0].is_a? Symbol
9
+ options = args[-1] if args[-1].is_a? Hash
10
+ self.send("_#{options[:controller]||self.controller_name}_#{__method__}_#{name||self.controller_name}_tag")
11
+ end
12
+
13
+ # Generates all the fields as defined in controller with the <form> tag.
14
+ # If no name is given, it uses the name of the controller to find the corresponding model
15
+ def formize_fields(*args)
16
+ name, options = nil, {}
17
+ name = args[0] if args[0].is_a? Symbol
18
+ options = args[-1] if args[-1].is_a? Hash
19
+ self.send("_#{options[:controller]||self.controller_name}_#{__method__}_#{name||self.controller_name}_tag")
20
+ end
21
+
22
+ # Permits to use content_tag in helpers with easy add
23
+ def hard_content_tag(name, options={}, escape=true, &block)
24
+ content = ''
25
+ yield content
26
+ return content_tag(name, content, options, escape)
27
+ end
28
+
29
+ # Returns a list of radio buttons for specified attribute (identified by +method+)
30
+ # on an object assigned to the template (identified by +object_name+). It works like +select+
31
+ def radio(object_name, method, choices, options = {}, html_options = {})
32
+ html = ""
33
+ html_options[:class] ||= :rad
34
+ for choice in choices
35
+ html << content_tag(:span, radio_button(object_name, method, choice[1]) + '&nbsp;'.html_safe + label(object_name, method, choice[0], :value=>choice[1]), html_options)
36
+ end
37
+ return html
38
+ end
39
+
40
+ # Returns a text field which has the same behavior of +select+ but with a search
41
+ # action which permits to find easily in very long lists...
42
+ def unroll(object_name, method, choices, options = {}, input_options={}, html_options = {})
43
+ object = instance_variable_get("@#{object_name}")
44
+ label = options[:label]
45
+ if label.is_a?(String) or label.is_a?(Symbol)
46
+ label = Proc.new{|x| x.send(label)}
47
+ elsif !label.is_a?(Proc)
48
+ label = Proc.new{|x| x.inspect}
49
+ end
50
+ html = ""
51
+ html << hidden_field(object_name, method, input_options)
52
+ html << tag(:input, :type=>:text, "data-unroll"=>url_for(choices), "data-value-container"=>"#{object_name}_#{method}", :value=>label.call(object.send(method.to_s.gsub(/_id$/, ''))), :size=>html_options.delete(:size)||32)
53
+ return content_tag(:span, html, html_options)
54
+ end
55
+
56
+
57
+
58
+ end
59
+
60
+ end
61
+
62
+ ActionView::Base.send :include, Formize::FormHelper