extjs-mvc 0.2.8 → 0.3.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.
data/README.rdoc CHANGED
@@ -45,15 +45,42 @@ fields with will be used to render the <tt>Ext.data.Record.create</tt> field-def
45
45
 
46
46
  After including the model mixin <tt>ExtJS::Model</tt>, try typing the following in <tt>irb</tt> console:
47
47
  >> User.extjs_record
48
- => { "idProperty"=>"id", "fields"=>[
49
- {:type=>:int, :allowBlank=>true, :name=>"id"},
50
- {:type=>:string, :allowBlank=>false, :name=>"first"},
51
- {:type=>:string, :allowBlank=>false, :name=>"last"},
52
- {:type=>:string, :allowBlank=>false, :name=>"email"}
48
+ => { :idProperty=>"id", :fields=>[
49
+ {:type=>'int', :allowBlank=>true, :name=>"id"},
50
+ {:type=>'string', :allowBlank=>false, :name=>"first", :defaultValue => nil},
51
+ {:type=>'string', :allowBlank=>false, :name=>"last", :defaultValue => nil},
52
+ {:type=>'string', :allowBlank=>false, :name=>"email", :defaultValue => nil}
53
53
  ]}
54
54
 
55
55
  An auto-generated <tt>Ext.data.JsonReader</tt> configuration!
56
56
 
57
+
58
+ You can also define different sets of fields for different representation of your model.
59
+
60
+ E.g. with the following definition:
61
+
62
+ class User < ActiveRecord::Base
63
+ include ExtJS::Model
64
+
65
+ extjs_fieldset :grid, [:name, :description, :company => [:name, :description]]
66
+ extjs_fieldset :combo, [:full_name]
67
+
68
+ def full_name
69
+ "#{first_name} #{name}"
70
+ end
71
+ end
72
+
73
+ You can get store configs for both representations with
74
+ User.extjs_record(:grid)
75
+ or
76
+ User.extjs_record(:combo)
77
+
78
+ And the corresponding data for the representations with
79
+ User.first.to_record(:grid)
80
+ or
81
+ User.first.to_record(:combo)
82
+
83
+
57
84
  === An ActionController mixin: ExtJS::Controller
58
85
  The <tt>extjs-mvc</tt> Gem includes a framework agnostic Controller mixin which works with both Rails and Merb. Include this mixin into any controller which will need to generate an <tt>Ext.data.Store</tt>.
59
86
  <b>usage:</b>
@@ -112,6 +139,25 @@ Now render a store in an erb template:
112
139
 
113
140
  %= @store.render %
114
141
 
142
+ === A Testing Mixin: ExtJS::TestMacros
143
+ The <tt>extjs-mvc</tt> Gem includes a small set of testing macros to help unit-test models.
144
+ This requires the 'Shoulda' gem from thoughtbot. Include this mixin inside the
145
+ <tt>ActiveSupport::TestCase</tt> class in <tt>test/test_helper.rb</tt>
146
+
147
+ ==== Usage
148
+ <tt>test/test_helper.rb</tt>
149
+ class ActiveSupport::TestCase
150
+ extend ExtJS::TestMacros
151
+ #...
152
+ end
153
+
154
+ In individual model unit tests:
155
+ class ModelTest < ActiveSupport::TestCase
156
+ should_require_extjs_fields :name, :email, :city
157
+ #...
158
+ #other tests
159
+ end
160
+
115
161
 
116
162
  == Note on Patches/Pull Requests
117
163
 
data/Rakefile CHANGED
@@ -5,17 +5,20 @@ begin
5
5
  require 'jeweler'
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = "extjs-mvc"
8
- gem.summary = %Q{Ruby tools for ExtJS development}
8
+ gem.summary = %Q{Ruby ORM tools to assist with rendering Ext.data.Store}
9
9
  gem.description = %Q{MVC tools to assist with ExtJS development in Rails and Merb}
10
10
  gem.email = "christocracy@gmail.com"
11
11
  gem.homepage = "http://github.com/extjs/mvc"
12
12
  gem.authors = ["Chris Scott"]
13
- gem.add_development_dependency "thoughtbot-shoulda"
13
+ gem.add_development_dependency "shoulda"
14
+ gem.add_development_dependency "mocha"
15
+
14
16
  gem.test_files = []
15
17
  gem.files = FileList["[A-Z]*", "{bin,generators,lib,test}/**/*", 'lib/jeweler/templates/.gitignore']
16
18
 
17
19
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
20
  end
21
+ Jeweler::GemcutterTasks.new
19
22
  rescue LoadError
20
23
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
21
24
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.8
1
+ 0.3.0
data/lib/extjs-mvc.rb CHANGED
@@ -33,12 +33,6 @@ module ExtJS
33
33
 
34
34
  # Controller mixin. Works for both Rails and Merb.
35
35
  require 'controller/controller'
36
-
37
36
  end
38
37
  end
39
38
 
40
- #require 'test/macros'
41
-
42
- #class ActiveSupport::TestCase < Test::Unit::TestCase
43
- # extend ExtJS::TestMacros
44
- #end
@@ -10,11 +10,11 @@ module ExtJS
10
10
  end
11
11
 
12
12
  def extjs_column_names
13
- self.column_names
13
+ self.column_names.map(&:to_sym)
14
14
  end
15
15
 
16
16
  def extjs_columns_hash
17
- self.columns_hash
17
+ self.columns_hash.symbolize_keys
18
18
  end
19
19
 
20
20
  ##
@@ -27,21 +27,38 @@ module ExtJS
27
27
  end
28
28
 
29
29
  ##
30
- # determine datatype of supplied Column object
30
+ # returns the default value
31
31
  # @param {ActiveRecord::ConnectionAdapters::Column}
32
+ # @return {Mixed}
33
+ #
34
+ def extjs_default(col)
35
+ col.default
36
+ end
37
+
38
+ ##
39
+ # returns the corresponding column name of the type column for a polymorphic association
40
+ # @param {String/Symbol} the id column name for this association
32
41
  # @return {Symbol}
42
+ def extjs_polymorphic_type(id_column_name)
43
+ id_column_name.to_s.gsub(/_id\Z/, '_type').to_sym
44
+ end
45
+
46
+ ##
47
+ # determine datatype of supplied Column object
48
+ # @param {ActiveRecord::ConnectionAdapters::Column}
49
+ # @return {String}
33
50
  #
34
51
  def extjs_type(col)
35
- type = col.type
52
+ type = col.type.to_s
36
53
  case type
37
- when :datetime, :date, :time, :timestamp
38
- type = :date
39
- when :text
40
- type = :string
41
- when :integer
42
- type = :int
43
- when :decimal
44
- type = :float
54
+ when "datetime", "date", "time", "timestamp"
55
+ type = "date"
56
+ when "text"
57
+ type = "string"
58
+ when "integer"
59
+ type = "int"
60
+ when "decimal"
61
+ type = "float"
45
62
  end
46
63
  type
47
64
  end
@@ -51,20 +68,21 @@ module ExtJS
51
68
  # @return {Array}
52
69
  #
53
70
  def extjs_associations
54
- if @extjs_associations.nil?
55
- @extjs_associations = {}
71
+ #if @extjs_associations.nil?
72
+ extjs_associations = {}
56
73
  self.reflections.keys.each do |key|
57
74
  assn = self.reflections[key]
58
75
  type = (assn.macro === :has_many || assn.macro === :has_and_belongs_to_many) ? :many : assn.macro
59
- @extjs_associations[key.to_sym] = {
60
- :name => key,
76
+ extjs_associations[key.to_sym] = {
77
+ :name => key.to_sym,
61
78
  :type => type,
62
- :class => assn.class_name.constantize,
63
- :foreign_key => assn.association_foreign_key
79
+ :class => assn.options[:polymorphic] ? nil : assn.class_name.constantize,
80
+ :foreign_key => assn.association_foreign_key.to_sym,
81
+ :is_polymorphic => !!assn.options[:polymorphic]
64
82
  }
65
83
  end
66
- end
67
- @extjs_associations
84
+ #end
85
+ extjs_associations
68
86
  end
69
87
  end
70
88
  end
data/lib/model/base.rb CHANGED
@@ -4,20 +4,14 @@ module ExtJS
4
4
  def self.included(model)
5
5
  model.send(:extend, ClassMethods)
6
6
  model.send(:include, InstanceMethods)
7
- model.class_eval do
8
- ##
9
- # @config {Array} List of DataReader fields to render. This should probably not be a cattr_accessor
10
- # since it's only used internally. The user adds fields via the Class method extjs_fields instead.
11
- #
12
- cattr_accessor :extjs_record_fields
13
- ##
14
- # @config {String} extjs_mapping_template This a template used to render mapped field-names.
15
- # One could use the Rails standard "{association}[{property}]" as well.
16
- #
17
- cattr_accessor :extjs_mapping_template
18
- end
19
- model.extjs_record_fields = []
20
- model.extjs_mapping_template = "_{property}"
7
+ ##
8
+ # @config {String} extjs_parent_trail_template This a template used to render mapped field-names.
9
+ # Default is Proc.new{ |field_name| "_#{field_name}" }
10
+ # You could also use the Rails standard
11
+ # Proc.new{ |field_name| "[#{field_name}]" }
12
+ #
13
+ model.cattr_accessor :extjs_parent_trail_template
14
+ model.extjs_parent_trail_template = Proc.new{ |field_name| "_#{field_name}" } if model.extjs_parent_trail_template.nil?
21
15
  end
22
16
 
23
17
  ##
@@ -30,41 +24,55 @@ module ExtJS
30
24
  # @params {Mixed} params A list of fields to use instead of this Class's extjs_record_fields
31
25
  #
32
26
  def to_record(*params)
33
- if self.class.extjs_record_fields.empty?
34
- self.class.extjs_fields(*self.class.extjs_column_names)
27
+ fieldset, params = self.class.extjs_extract_fieldset! params
28
+
29
+ fields = []
30
+ if params.empty?
31
+ fields = self.class.extjs_get_fields_for_fieldset(fieldset)
32
+ else
33
+ fields = self.class.process_fields(*params)
35
34
  end
36
35
 
37
- fields = (params.empty?) ? self.class.extjs_record_fields : self.class.process_fields(*params)
38
36
  assns = self.class.extjs_associations
39
37
  pk = self.class.extjs_primary_key
40
38
 
41
39
  # build the initial field data-hash
42
- data = {pk.to_s => self.send(pk)}
40
+ data = {pk => self.send(pk)}
43
41
 
44
42
  fields.each do |field|
45
- if refl = assns[field[:name]] || assns[field[:name].to_sym]
46
- if refl[:type] === :belongs_to
47
- assn = self.send(field[:name])
48
-
49
- if assn.respond_to?(:to_record)
50
- data[field[:name]] = assn.to_record field[:fields]
51
- elsif (field[:fields])
52
- data[field[:name]] = {}
53
- field[:fields].each do |property|
54
- data[field[:name]][property] = assn.send(property) if assn.respond_to?(property)
43
+ next if data.has_key? field[:name] # already processed (e.g. explicit mentioning of :id)
44
+
45
+ value = nil
46
+ if association_reflection = assns[field[:name]] # if field is an association
47
+ association = self.send(field[:name])
48
+ case association_reflection[:type]
49
+ when :belongs_to
50
+ if association.respond_to? :to_record
51
+ assn_fields = field[:fields]
52
+ if assn_fields.nil?
53
+ assn_fields = association.class.extjs_get_fields_for_fieldset(field.fetch(:fieldset, fieldset))
55
54
  end
55
+ value = association.to_record *assn_fields
56
56
  else
57
- data[field[:name]] = {} # belongs_to assn that doesn't respond to to_record and no fields list
57
+ value = {}
58
+ (field[:fields]||[]).each do |sub_field|
59
+ value[sub_field[:name]] = association.send(sub_field[:name]) if association.respond_to? sub_field[:name]
60
+ end
58
61
  end
59
62
  # Append associations foreign_key to data
60
- data[refl[:foreign_key].to_s] = self.send(refl[:foreign_key])
61
- elsif refl[:type] === :many
62
- data[field[:name]] = self.send(field[:name]).collect {|r| r.to_record} #CAREFUL!!!!!!!!!!!!1
63
+ data[association_reflection[:foreign_key]] = self.send(association_reflection[:foreign_key])
64
+ if association_reflection[:is_polymorphic]
65
+ foreign_type = self.class.extjs_polymorphic_type(association_reflection[:foreign_key])
66
+ data[foreign_type] = self.send(foreign_type)
67
+ end
68
+ when :many
69
+ value = association.collect { |r| r.to_record } # use carefully, can get HUGE
63
70
  end
64
- else
71
+ else # not an association -> get the method's value
65
72
  value = self.send(field[:name])
66
- data[field[:name]] = value.respond_to?(:to_record) ? value.to_record : value
73
+ value = value.to_record if value.respond_to? :to_record
67
74
  end
75
+ data[field[:name]] = value
68
76
  end
69
77
  data
70
78
  end
@@ -79,49 +87,66 @@ module ExtJS
79
87
  # eg: {name:'foo', type: 'string'}
80
88
  #
81
89
  def extjs_record(*fields)
82
- if self.extjs_record_fields.empty?
83
- self.extjs_fields(*self.extjs_column_names)
90
+ fieldset, fields = self.extjs_extract_fieldset! fields
91
+
92
+ if fields.empty?
93
+ fields = self.extjs_get_fields_for_fieldset(fieldset)
94
+ else
95
+ fields = self.process_fields(*fields)
84
96
  end
85
97
 
86
98
  associations = self.extjs_associations
87
99
  columns = self.extjs_columns_hash
88
- fields = fields.empty? ? self.extjs_record_fields : self.process_fields(*fields)
89
100
  pk = self.extjs_primary_key
90
101
  rs = []
91
102
 
92
103
  fields.each do |field|
93
- field = field.dup
104
+ field = Marshal.load(Marshal.dump(field)) # making a deep copy
94
105
 
95
- if col = columns[field[:name]] || columns[field[:name].to_sym] # <-- column on this model
106
+ if col = columns[field[:name]] # <-- column on this model
96
107
  rs << self.extjs_field(field, col)
97
- elsif assn = associations[field[:name]] || associations[field[:name].to_sym]
98
- assn_fields = field.delete(:fields) || nil
108
+ elsif assn = associations[field[:name]]
109
+ assn_fields = field[:fields]
99
110
  if assn[:class].respond_to?(:extjs_record) # <-- exec extjs_record on assn Model.
100
- record = assn[:class].extjs_record(assn_fields)
101
- rs.concat(record["fields"].collect {|assn_field|
102
- extjs_field(assn_field, :mapping => field[:name], "allowBlank" => true) # <-- allowBlank on associated data?
103
- })
104
- elsif assn_fields # <-- :parent => [:id, :name]
105
- rs.concat(assn_fields.collect {|assn_field|
106
- extjs_field(assn_field, :mapping => field[:name], "allowBlank" => true)
111
+ if assn_fields.nil?
112
+ assn_fields = assn[:class].extjs_get_fields_for_fieldset(field.fetch(:fieldset, fieldset))
113
+ end
114
+ record = assn[:class].extjs_record(field.fetch(:fieldset, fieldset), assn_fields)
115
+ rs.concat(record[:fields].collect { |assn_field|
116
+ self.extjs_field(assn_field, :parent_trail => field[:name], :mapping => field[:name], :allowBlank => true) # <-- allowBlank on associated data?
107
117
  })
118
+ elsif assn_fields # <-- :parent => [:id, :name, :sub => [:id, :name]]
119
+ field_collector = Proc.new do |parent_trail, mapping, assn_field|
120
+ if assn_field.is_a?(Hash) && assn_field.keys.size == 1 && assn_field.keys[0].is_a?(Symbol) && assn_field.values[0].is_a?(Array)
121
+ field_collector.call(parent_trail.to_s + self.extjs_parent_trail_template.call(assn_field.keys.first), "#{mapping}.#{assn_field.keys.first}", assn_field.values.first)
122
+ else
123
+ self.extjs_field(assn_field, :parent_trail => parent_trail, :mapping => mapping, :allowBlank => true)
124
+ end
125
+ end
126
+ rs.concat(assn_fields.collect { |assn_field| field_collector.call(field[:name], field[:name], assn_field) })
108
127
  else
109
128
  rs << extjs_field(field)
110
129
  end
111
130
 
112
131
  # attach association's foreign_key if not already included.
113
- if (col = columns[assn[:foreign_key]] || columns[assn[:foreign_key].to_s]) && !rs.include?({:name => assn[:foreign_key].to_s})
114
- rs << extjs_field({:name => assn[:foreign_key]}, col)
132
+ if columns.has_key?(assn[:foreign_key]) && !rs.any? { |r| r[:name] == assn[:foreign_key] }
133
+ rs << extjs_field({:name => assn[:foreign_key]}, columns[assn[:foreign_key]])
134
+ end
135
+ # attach association's type if polymorphic association and not alredy included
136
+ if assn[:is_polymorphic]
137
+ foreign_type = self.extjs_polymorphic_type(assn[:foreign_key])
138
+ if columns.has_key?(foreign_type) && !rs.any? { |r| r[:name] == foreign_type }
139
+ rs << extjs_field({:name => foreign_type}, columns[foreign_type])
140
+ end
115
141
  end
116
-
117
142
  else # property is a method?
118
143
  rs << extjs_field(field)
119
144
  end
120
145
  end
121
146
 
122
147
  return {
123
- "fields" => rs,
124
- "idProperty" => pk
148
+ :fields => rs,
149
+ :idProperty => pk
125
150
  }
126
151
  end
127
152
 
@@ -129,67 +154,98 @@ module ExtJS
129
154
  # meant to be used within a Model to define the extjs record fields.
130
155
  # eg:
131
156
  # class User
132
- # extjs_fields :first, :last, :email => {"sortDir" => "ASC"}, :company => [:id, :name]
157
+ # extjs_fieldset :grid, [:first, :last, :email => {"sortDir" => "ASC"}, :company => [:id, :name]]
158
+ # end
159
+ # or
160
+ # class User
161
+ # extjs_fieldset :last, :email => {"sortDir" => "ASC"}, :company => [:id, :name] # => implies fieldset name :default
133
162
  # end
134
163
  #
164
+ def extjs_fieldset(*params)
165
+ fieldset, params = self.extjs_extract_fieldset! params
166
+ # creates a method storing the fieldset-to-field association
167
+ # using a method and not an class-level variable because the latter
168
+ # is bogus when we deal with inheritance
169
+ var_name = :"@extjs_fieldsets__#{fieldset}"
170
+ self.instance_variable_set( var_name, self.process_fields(*params) )
171
+ end
172
+
173
+ def extjs_get_fields_for_fieldset(fieldset)
174
+ var_name = :"@extjs_fieldsets__#{fieldset}"
175
+ super_value = nil
176
+ unless self.instance_variable_get( var_name )
177
+ if self.superclass.respond_to? :extjs_get_fields_for_fieldset
178
+ super_value = self.superclass.extjs_get_fields_for_fieldset(fieldset)
179
+ end
180
+ self.extjs_fieldset(fieldset, self.extjs_column_names) unless super_value
181
+ end
182
+ super_value || self.instance_variable_get( var_name )
183
+ end
184
+
185
+ ##
186
+ # shortcut to define the default fieldset. For backwards-compatibility.
187
+ #
135
188
  def extjs_fields(*params)
136
- self.extjs_record_fields = self.process_fields(*params)
189
+ self.extjs_fieldset(:default, params)
137
190
  end
138
191
 
139
192
  ##
140
193
  # Prepare a field configuration list into a normalized array of Hashes, {:name => "field_name"}
141
194
  # @param {Mixed} params
142
- # @return Array
195
+ # @return {Array} of Hashes
143
196
  #
144
- def process_fields(*params)
145
- params = [] if params.first.nil?
146
- options = params.extract_options!
147
-
148
- # Return immediately if pre-processed fields are detected.
149
- # ie: [ [{:name => 'foo'}, {:name => 'bar'}] ]
150
- # This is to handle the case where extjs_record and to_record are called recursively, in which case
151
- # these fields have already been processed.
152
- #
153
- #if params.length === 1 && params.first.kind_of?(Array) && !params.first.empty?
154
- # return params.first
155
- #end
156
-
197
+ def process_fields(*params)
157
198
  fields = []
158
- if !options.keys.empty?
159
- if excludes = options.delete(:exclude)
160
- fields = self.process_fields(self.extjs_column_names.reject {|c| excludes.find {|ex| c === ex.to_s}}.collect {|c| c})
161
- elsif only = options.delete(:only)
162
- fields = self.process_fields(only)
163
- else
164
- options.keys.each do |k| # <-- :email => {"sortDir" => "ASC"}
165
- if options[k].is_a? Hash
166
- options[k][:name] = k.to_s
167
- fields << options[k]
168
- elsif options[k].is_a? Array # <-- :parent => [:id, :name]
169
- fields << {
170
- :name => k.to_s,
171
- :fields => process_fields(*options[k])
172
- }
173
- end
174
- end
199
+ if params.size == 1 && params.last.is_a?(Hash) # peek into argument to see if its an option hash
200
+ options = params.last
201
+ if options.has_key?(:exclude) && options[:exclude].is_a?(Array)
202
+ return self.process_fields(*(self.extjs_column_names - options[:exclude].map(&:to_sym)))
203
+ elsif options.has_key?(:only) && options[:only].is_a?(Array)
204
+ return self.process_fields(*options[:only])
175
205
  end
176
- elsif params.empty?
177
- return self.extjs_record_fields
178
206
  end
179
207
 
180
- unless params.empty?
181
- params = params.first if params.length == 1 && params.first.kind_of?(Array) && !params.first.empty?
182
- params.each do |f|
183
- if f.kind_of?(Hash)
208
+ params = self.extjs_column_names if params.empty?
209
+
210
+ associations = extjs_associations
211
+
212
+ params.each do |f|
213
+ if f.kind_of?(Hash)
214
+ if f.keys.size == 1 && f.keys[0].is_a?(Symbol) && f.values[0].is_a?(Array) # {:association => [:field1, :field2]}
215
+ fields << {
216
+ :name => f.keys[0],
217
+ :fields => process_fields(*f.values[0])
218
+ }
219
+ elsif f.keys.size == 1 && f.keys[0].is_a?(Symbol) && f.values[0].is_a?(Hash) # {:field => {:sortDir => 'ASC'}}
220
+ fields << f.values[0].update(:name => f.keys[0])
221
+ elsif f.has_key?(:name) # already a valid Hash, just copy it over
184
222
  fields << f
185
223
  else
186
- fields << {:name => f.to_s}
224
+ raise ArgumentError, "encountered a Hash that I don't know anyting to do with `#{f.inspect}:#{f.class}`"
187
225
  end
226
+ else # should be a String or Symbol
227
+ puts params.inspect if f.nil?
228
+ fields << {:name => f.to_sym}
188
229
  end
189
230
  end
231
+
190
232
  fields
191
233
  end
192
234
 
235
+ ##
236
+ # returns the fieldset from the arguments.
237
+ # @return [{Symbol}, Array]
238
+ def extjs_extract_fieldset! arguments
239
+ fieldset = :default
240
+ if arguments.size > 1 && arguments[0].is_a?(Symbol) && arguments[1].is_a?(Array)
241
+ fieldset = arguments.shift
242
+ arguments = arguments[0]
243
+ elsif arguments.size == 1 && arguments[0].is_a?(Symbol)
244
+ fieldset = arguments.shift
245
+ end
246
+ [fieldset, arguments]
247
+ end
248
+
193
249
  ##
194
250
  # Render a column-config object
195
251
  # @param {Hash/Column} field Field-configuration Hash, probably has :name already set and possibly Ext.data.Field options.
@@ -197,41 +253,48 @@ module ExtJS
197
253
  #
198
254
  def extjs_field(field, config=nil)
199
255
  if config.kind_of? Hash
200
- if mapping = config.delete(:mapping)
256
+ if config.has_key?(:mapping) && config.has_key?(:parent_trail)
201
257
  field.update( # <-- We use a template for rendering mapped field-names.
202
- :name => mapping + self.extjs_mapping_template.gsub(/\{property\}/, field[:name]),
203
- "mapping" => "#{mapping.to_s}.#{field[:name]}"
258
+ :name => config[:parent_trail].to_s + self.extjs_parent_trail_template.call(field[:name]),
259
+ :mapping => "#{config[:mapping]}.#{field[:name]}"
204
260
  )
205
261
  end
206
- field.update(config) unless config.keys.empty?
262
+ field.update(config.except(:mapping, :parent_trail))
207
263
  elsif !config.nil? # <-- Hopfully an ORM Column object.
208
264
  field.update(
209
- "allowBlank" => self.extjs_allow_blank(config),
210
- "type" => self.extjs_type(config)
265
+ :allowBlank => self.extjs_allow_blank(config),
266
+ :type => self.extjs_type(config),
267
+ :defaultValue => self.extjs_default(config)
211
268
  )
212
- field["dateFormat"] = "c" if field["type"] === :date && field["dateFormat"].nil? # <-- ugly hack for date
269
+ field[:dateFormat] = "c" if field[:type] === "date" && field[:dateFormat].nil? # <-- ugly hack for date
213
270
  end
214
- field.update("type" => "auto") if field["type"].nil?
271
+ field.update(:type => "auto") if field[:type].nil?
272
+ # convert Symbol values to String values
273
+ field.keys.each do |k|
274
+ raise ArgumentError, "extjs_field expects a Hash as first parameter with all it's keys Symbols. Found key #{k.inspect}:#{k.class.to_s}" unless k.is_a?(Symbol)
275
+ field[k] = field[k].to_s if field[k].is_a?(Symbol)
276
+ end
215
277
  field
216
278
  end
279
+
217
280
 
218
- ##
219
- # Returns an array of symbolized association names that will be referenced by a call to to_record
220
- # i.e. [:parent1, :parent2]
221
- #
222
- def extjs_used_associations
223
- if @extjs_used_associations.nil?
224
- assoc = []
225
- self.extjs_record_fields.each do |f|
226
- #This needs to be the first condition because the others will break if f is an Array
227
- if extjs_associations[f[:name]]
228
- assoc << f[:name]
229
- end
230
- end
231
- @extjs_used_associations = assoc.uniq
232
- end
233
- @extjs_used_associations
234
- end
281
+ # ##
282
+ # # Returns an array of symbolized association names that will be referenced by a call to to_record
283
+ # # i.e. [:parent1, :parent2]
284
+ # #
285
+ # def extjs_used_associations
286
+ # if @extjs_used_associations.nil?
287
+ # assoc = []
288
+ # self.extjs_record_fields.each do |f|
289
+ # #This needs to be the first condition because the others will break if f is an Array
290
+ # if extjs_associations[f[:name]]
291
+ # assoc << f[:name]
292
+ # end
293
+ # end
294
+ # @extjs_used_associations = assoc.uniq
295
+ # end
296
+ # @extjs_used_associations
297
+ # end
235
298
  end
236
299
  end
237
300
  end
@@ -52,7 +52,8 @@ module ExtJS
52
52
  :name => key,
53
53
  :type => type = (assn.options[:max].nil? && assn.options[:min].nil?) ? :belongs_to : (assn.options[:max] > 1) ? :many : nil ,
54
54
  :class => assn.parent_model,
55
- :foreign_key => assn.child_key.first.name
55
+ :foreign_key => assn.child_key.first.name,
56
+ :is_polymorphic => false # <-- No impl. for DM is_polymorphic. Anyone care to implement this?
56
57
  }
57
58
  end
58
59
  end
@@ -28,7 +28,8 @@ module ExtJS
28
28
  :name => key,
29
29
  :type => self.associations[key].type,
30
30
  :class => self.associations[key].class_name.constantize,
31
- :foreign_key => self.associations[key].foreign_key
31
+ :foreign_key => self.associations[key].foreign_key,
32
+ :is_polymorphic => false # <-- no impl. for MM is_polymorhpic yet. Anyone care to implement this?
32
33
  }
33
34
  end
34
35
  end