extjs-mvc 0.2.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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