netzke-basepack 0.7.4 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/.travis.yml +11 -0
  2. data/CHANGELOG.rdoc +10 -0
  3. data/README.md +36 -2
  4. data/Rakefile +1 -3
  5. data/config/ci/before-travis.sh +28 -0
  6. data/lib/netzke/active_record.rb +10 -8
  7. data/lib/netzke/active_record/attributes.rb +28 -17
  8. data/lib/netzke/active_record/relation_extensions.rb +3 -1
  9. data/lib/netzke/basepack.rb +10 -2
  10. data/lib/netzke/basepack/action_column.rb +6 -8
  11. data/lib/netzke/basepack/data_accessor.rb +11 -174
  12. data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +164 -0
  13. data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +279 -0
  14. data/lib/netzke/basepack/data_adapters/data_mapper_adapter.rb +264 -0
  15. data/lib/netzke/basepack/data_adapters/sequel_adapter.rb +260 -0
  16. data/lib/netzke/basepack/form_panel.rb +3 -3
  17. data/lib/netzke/basepack/form_panel/fields.rb +6 -10
  18. data/lib/netzke/basepack/form_panel/javascripts/form_panel.js +1 -0
  19. data/lib/netzke/basepack/form_panel/services.rb +15 -16
  20. data/lib/netzke/basepack/grid_panel.rb +16 -10
  21. data/lib/netzke/basepack/grid_panel/columns.rb +6 -7
  22. data/lib/netzke/basepack/grid_panel/javascripts/event_handling.js +29 -27
  23. data/lib/netzke/basepack/grid_panel/services.rb +13 -90
  24. data/lib/netzke/basepack/paging_form_panel.rb +3 -3
  25. data/lib/netzke/basepack/query_builder.rb +2 -0
  26. data/lib/netzke/basepack/query_builder/javascripts/query_builder.js +29 -19
  27. data/lib/netzke/basepack/search_panel.rb +6 -3
  28. data/lib/netzke/basepack/search_panel/javascripts/search_panel.js +2 -1
  29. data/lib/netzke/basepack/search_window.rb +2 -1
  30. data/lib/netzke/basepack/version.rb +1 -1
  31. data/lib/netzke/data_mapper.rb +18 -0
  32. data/lib/netzke/data_mapper/attributes.rb +273 -0
  33. data/lib/netzke/data_mapper/combobox_options.rb +11 -0
  34. data/lib/netzke/data_mapper/relation_extensions.rb +38 -0
  35. data/lib/netzke/sequel.rb +18 -0
  36. data/lib/netzke/sequel/attributes.rb +274 -0
  37. data/lib/netzke/sequel/combobox_options.rb +10 -0
  38. data/lib/netzke/sequel/relation_extensions.rb +40 -0
  39. data/netzke-basepack.gemspec +24 -13
  40. data/test/basepack_test_app/Gemfile +33 -8
  41. data/test/basepack_test_app/Gemfile.lock +98 -79
  42. data/test/basepack_test_app/Guardfile +46 -0
  43. data/test/basepack_test_app/app/components/book_grid_with_persistence.rb +3 -0
  44. data/test/basepack_test_app/app/components/extras/book_presentation.rb +10 -3
  45. data/test/basepack_test_app/app/models/address.rb +27 -1
  46. data/test/basepack_test_app/app/models/author.rb +28 -0
  47. data/test/basepack_test_app/app/models/book.rb +43 -0
  48. data/test/basepack_test_app/app/models/book_with_custom_primary_key.rb +22 -0
  49. data/test/basepack_test_app/app/models/role.rb +21 -0
  50. data/test/basepack_test_app/app/models/user.rb +24 -0
  51. data/test/basepack_test_app/config/database.yml.sample +11 -10
  52. data/test/basepack_test_app/config/database.yml.travis +15 -0
  53. data/test/basepack_test_app/config/initializers/data_mapper_logging.rb +3 -0
  54. data/test/basepack_test_app/config/initializers/sequel.rb +26 -0
  55. data/test/basepack_test_app/db/schema.rb +0 -3
  56. data/test/basepack_test_app/features/grid_panel.feature +28 -8
  57. data/test/basepack_test_app/features/grid_sorting.feature +6 -6
  58. data/test/basepack_test_app/features/paging_form_panel.feature +13 -13
  59. data/test/basepack_test_app/features/search_in_grid.feature +31 -31
  60. data/test/basepack_test_app/features/step_definitions/generic_steps.rb +3 -1
  61. data/test/basepack_test_app/features/support/env.rb +17 -4
  62. data/test/basepack_test_app/lib/tasks/travis.rake +7 -0
  63. data/test/basepack_test_app/spec/components/form_panel_spec.rb +2 -2
  64. data/test/basepack_test_app/spec/data_adapter/adapter_spec.rb +68 -0
  65. data/test/basepack_test_app/spec/{active_record → data_adapter}/attributes_spec.rb +12 -4
  66. data/test/basepack_test_app/spec/data_adapter/relation_extensions_spec.rb +125 -0
  67. data/test/basepack_test_app/spec/spec_helper.rb +9 -0
  68. data/test/unit/active_record_basepack_test.rb +1 -1
  69. data/test/unit/grid_panel_test.rb +1 -1
  70. metadata +26 -31
  71. data/app/models/netzke_field_list.rb +0 -261
  72. data/app/models/netzke_model_attr_list.rb +0 -21
  73. data/app/models/netzke_persistent_array_auto_model.rb +0 -57
  74. data/test/basepack_test_app/spec/active_record/relation_extensions_spec.rb +0 -44
@@ -0,0 +1,260 @@
1
+ module Netzke::Basepack::DataAdapters
2
+ class SequelAdapter < AbstractAdapter
3
+ def self.for_class?(model_class)
4
+ model_class <= Sequel::Model
5
+ end
6
+
7
+ def get_records(params, columns=[])
8
+ get_dataset(params, columns).all
9
+ end
10
+
11
+ def count_records(params, columns=[])
12
+ # dont pass columns, JOINs will be done as necessary for filters
13
+ get_dataset(params, [], true).count
14
+ end
15
+
16
+ def map_type type
17
+ type
18
+ end
19
+
20
+ def get_assoc_property_type assoc_name, prop_name
21
+ db_schema=class_for(assoc_name.to_sym).db_schema
22
+ # return nil if prop_name not present in db schema (virtual column)
23
+ db_schema[prop_name.to_sym] ? db_schema[prop_name.to_sym][:type] : nil
24
+ end
25
+
26
+ # like get_assoc_property_type but for non-association columns
27
+ def get_property_type column
28
+ column[:type]
29
+ end
30
+
31
+ def column_virtual? c
32
+ assoc, method = c[:name].split '__'
33
+ if method
34
+ !class_for(assoc.to_sym).columns.include? method.to_sym
35
+ else
36
+ !@model_class.columns.include? assoc.to_sym
37
+ end
38
+ end
39
+
40
+ # Returns options for comboboxes in grids/forms
41
+ def combobox_options_for_column(column, method_options = {})
42
+ query = method_options[:query]
43
+
44
+ # First, check if we have options for this column defined in persistent storage
45
+ options = column[:combobox_options] && column[:combobox_options].split("\n")
46
+ if options
47
+ query ? options.select{ |o| o.index(/^#{query}/) }.map{ |el| [el] } : options
48
+ else
49
+ assoc_name, assoc_method = column[:name].split '__'
50
+
51
+ if assoc_name
52
+ # Options for an asssociation attribute
53
+ dataset = class_for(assoc_name)
54
+
55
+ dataset = dataset.extend_with(method_options[:scope]) if method_options[:scope]
56
+
57
+ if class_for(assoc_name).column_names.include?(assoc_method)
58
+ # apply query
59
+ dataset = dataset.where(assoc_method.to_sym.like("%#{query}%")) if query.present?
60
+ dataset.all.map{ |r| [r.id, r.send(assoc_method)] }
61
+ else
62
+ dataset.all.map{ |r| [r.id, r.send(assoc_method)] }.select{ |id,value| value =~ /^#{query}/ }
63
+ end
64
+ else
65
+ # Options for a non-association attribute
66
+ res=@model_class.netzke_combo_options_for(column[:name], method_options)
67
+
68
+ # ensure it is an array-in-array, as Ext will fail otherwise
69
+ raise RuntimeError, "netzke_combo_options_for should return an Array" unless res.kind_of? Array
70
+ return [[]] if res.empty?
71
+
72
+ unless res.first.kind_of? Array
73
+ res=res.map do |v|
74
+ [v]
75
+ end
76
+ end
77
+ return res
78
+ end
79
+ end
80
+ end
81
+
82
+ def foreign_key_for assoc_name
83
+ @model_class.association_reflection(assoc_name.to_sym)[:key].to_s
84
+ end
85
+
86
+ # Returns the model class for an association
87
+ def class_for assoc_name
88
+ @model_class.association_reflection(assoc_name.to_sym)[:class_name].constantize
89
+ end
90
+
91
+ def destroy(ids)
92
+ @model_class.where(:id => ids).destroy
93
+ end
94
+
95
+ def find_record(id)
96
+ @model_class[id]
97
+ end
98
+
99
+ # Build a hash of foreign keys and the associated model
100
+ def hash_fk_model
101
+ @model_class.all_association_reflections.inject({}) do |res, assoc|
102
+ res[assoc[:key]] = assoc[:class_name].constantize.model_name.underscore.to_sym
103
+ res
104
+ end
105
+ end
106
+
107
+ # TODO: is this possible with Sequel?
108
+ def move_records(params)
109
+ end
110
+
111
+ # give the data adapter the opportunity the set special options for
112
+ # saving
113
+ def save_record(record)
114
+ # don't raise an error on saving. basepack will evaluate record.errors
115
+ # to get validation errors
116
+ record.raise_on_save_failure = false
117
+ record.save
118
+ end
119
+
120
+ # give the data adapter the opporunity to process error messages
121
+ # must return an raay of the form ["Title can't be blank", "Foo can't be blank"]
122
+ def errors_array(record)
123
+ record.errors.to_a.inject([]) do |errors, error|
124
+ field, message = error
125
+ errors << "#{record.class.human_attribute_name(field)} #{message.join ', '}"
126
+ errors
127
+ end
128
+ end
129
+
130
+ # Needed for seed and tests
131
+ def last
132
+ @model_class.last
133
+ end
134
+
135
+ # Needed for seed and tests
136
+ def destroy_all
137
+ @model_class.destroy
138
+ end
139
+
140
+ private
141
+ def get_dataset params, columns, for_count=false
142
+ dataset = @model_class
143
+
144
+ graphed=[]
145
+
146
+ # Parses and applies grid column filters
147
+ #
148
+ # Example column grid data:
149
+ #
150
+ # {"0" => {
151
+ # "data" => {
152
+ # "type" => "numeric",
153
+ # "comparison" => "gt",
154
+ # "value" => 10 },
155
+ # "field" => "id"
156
+ # },
157
+ # "1" => {
158
+ # "data" => {
159
+ # "type" => "string",
160
+ # "value" => "pizza"
161
+ # },
162
+ # "field" => "food_name"
163
+ # }}
164
+ #
165
+
166
+ if params[:filter]
167
+ # these are still JSON-encoded due to the migration to Ext.direct
168
+ column_filter=JSON.parse(params[:filter])
169
+
170
+ column_filter.each do |v|
171
+ field = v["field"]
172
+ assoc, method = field.split('__')
173
+ if method
174
+ # when filtering on association's columns, we need to graph for LEFT OUTER JOIN
175
+ dataset = dataset.eager_graph assoc.to_sym unless graphed.include? assoc.to_sym
176
+ graphed << assoc.to_sym
177
+ end
178
+
179
+ value = v["value"]
180
+ type = v["type"]
181
+ op = v["comparison"]
182
+
183
+ if type == "string"
184
+ # strings are always LIKEd (case-insensitive)
185
+ dataset = dataset.filter field.to_sym.ilike("%#{value}%")
186
+ else
187
+ if type == "date"
188
+ # convert value to the DB date
189
+ value.match /(\d\d)\/(\d\d)\/(\d\d\d\d)/
190
+ value = "#{$3}-#{$1}-#{$2}"
191
+ end
192
+ # if it's NOT an association column, we need to qualify column name with model's table_name
193
+ qualified_column_name = method ? field.to_sym : field.to_sym.qualify(@model_class.table_name)
194
+ case op
195
+ when 'lt'
196
+ dataset = dataset.filter ":column < '#{value}'", :column => qualified_column_name
197
+ when 'gt'
198
+ dataset = dataset.filter ":column > '#{value}'", :column => qualified_column_name
199
+ else
200
+ dataset = dataset.filter qualified_column_name => value
201
+ end
202
+ end
203
+ end
204
+ end
205
+ # skip sorting, eager joining and paging if dataset is used for count
206
+ unless for_count
207
+ if params[:sort] && sort_params = params[:sort]
208
+ sort_params.each do |sort_param|
209
+ assoc, method = sort_param["property"].split("__")
210
+ dir = sort_param["direction"].downcase
211
+
212
+ # if a sorting scope is set, call the scope with the given direction
213
+ column = columns.detect { |c| c[:name] == sort_param["property"] }
214
+ if column.try(:'has_key?', :sorting_scope)
215
+ dataset = dataset.send(column[:sorting_scope].to_sym, dir.to_sym)
216
+ else
217
+ if method # sorting on associations column
218
+ # graph the association for LEFT OUTER JOIN
219
+ dataset = dataset.eager_graph(assoc.to_sym) unless graphed.include? assoc.to_sym
220
+ graphed << assoc.to_sym
221
+ end
222
+ # coincidentally, netzkes convention of specifying association's attributes
223
+ # i.e. "author__name" on Book matches sequel's convention
224
+ # so we can just pass symbolized property here
225
+ dataset = dataset.order(sort_param["property"].to_sym.send(dir))
226
+ end
227
+ end
228
+ end
229
+
230
+ # eager load the associations indicated by columns,
231
+ # but only if we didn't eager_graph them before (for ordering/filtering)
232
+ # because this saves a ID IN query
233
+ columns.each do |column|
234
+ if column[:name].index('__')
235
+ assoc, _ = column[:name].split('__')
236
+ dataset = dataset.eager(assoc.to_sym) unless graphed.include? assoc.to_sym
237
+ end
238
+ end
239
+
240
+ # apply paging
241
+ if params[:limit]
242
+ if params[:start]
243
+ dataset = dataset.limit params[:limit], params[:start]
244
+ else
245
+ dataset = dataset.limit params[:limit]
246
+ end
247
+ end
248
+ end
249
+
250
+ # apply scope
251
+ # need to symbolize_keys, because when the request is made from client-side (as opposed
252
+ # to server-side on inital render), the scope's keys are given as string {"author_id" => 1}
253
+ # If we give Sequel a filter like this, it will (correctly) do WHERE 'author_id' = 1 - note the quotes
254
+ # making the database match the string author_id to 1 and to the column.
255
+ dataset = dataset.extend_with(params[:scope].symbolize_keys) if params[:scope]
256
+ dataset
257
+ end
258
+ end
259
+
260
+ end
@@ -99,18 +99,18 @@ module Netzke
99
99
 
100
100
  def js_config
101
101
  super.tap do |res|
102
- res[:pri] = data_class && data_class.primary_key
102
+ res[:pri] = data_class && data_class.primary_key.to_s
103
103
  res[:record] = js_record_data if record
104
104
  end
105
105
  end
106
106
 
107
107
  # A hash of record data including the meta field
108
108
  def js_record_data
109
- record.to_hash(fields).merge(:_meta => meta_field).literalize_keys
109
+ record.netzke_hash(fields).merge(:_meta => meta_field).literalize_keys
110
110
  end
111
111
 
112
112
  def record
113
- @record ||= config[:record] || config[:record_id] && data_class && data_class.where(data_class.primary_key => config[:record_id]).first
113
+ @record ||= config[:record] || config[:record_id] && data_class && data_adapter.find_record(config[:record_id])
114
114
  end
115
115
 
116
116
  private
@@ -10,12 +10,11 @@ module Netzke
10
10
  @form_panel_items ||= begin
11
11
  res = normalize_fields(super || data_class && data_class.netzke_attributes || []) # netzke_attributes as default items
12
12
  # if primary key isn't there, insert it as first
13
- if data_class && !res.detect{ |f| f[:name] == data_class.primary_key}
13
+ if data_class && !res.detect{ |f| f[:name] == data_class.primary_key.to_s}
14
14
  primary_key_item = normalize_field(data_class.primary_key.to_sym)
15
15
  @fields_from_config[data_class.primary_key.to_sym] = primary_key_item
16
16
  res.insert(0, primary_key_item)
17
17
  end
18
-
19
18
  res
20
19
  end
21
20
  end
@@ -121,14 +120,11 @@ module Netzke
121
120
  def detect_association_with_method(c)
122
121
  if c[:name].index('__')
123
122
  assoc_name, method = c[:name].split('__').map(&:to_sym)
124
- if method && assoc = data_class.reflect_on_association(assoc_name)
125
- assoc_column = assoc.klass.columns_hash[method.to_s]
126
- assoc_method_type = assoc_column.try(:type)
127
- if c[:nested_attribute]
128
- c[:xtype] ||= xtype_for_attr_type(assoc_method_type)
129
- else
130
- c[:xtype] ||= assoc_method_type == :boolean ? xtype_for_attr_type(assoc_method_type) : xtype_for_association
131
- end
123
+ assoc_method_type = data_adapter.get_assoc_property_type(assoc_name, method)
124
+ if c[:nested_attribute]
125
+ c[:xtype] ||= xtype_for_attr_type(assoc_method_type)
126
+ else
127
+ c[:xtype] ||= assoc_method_type == :boolean ? xtype_for_attr_type(assoc_method_type) : xtype_for_association
132
128
  end
133
129
  end
134
130
  end
@@ -172,6 +172,7 @@
172
172
  }, this);
173
173
  }
174
174
  }
175
+ this.fireEvent('afterApply', this);
175
176
  },
176
177
 
177
178
  setFormValues: function(values){
@@ -18,7 +18,7 @@ module Netzke
18
18
  #
19
19
  # someForm.netzkeLoad({id: 100});
20
20
  endpoint :netzke_load do |params|
21
- @record = data_class && data_class.find_by_id(params[:id])
21
+ @record = data_class && data_adapter.find_record(params[:id])
22
22
  {:set_form_values => js_record_data}
23
23
  end
24
24
 
@@ -42,16 +42,19 @@ module Netzke
42
42
 
43
43
  # Returns array of form values according to the configured columns
44
44
  # def array_of_values
45
- # @record && @record.to_array(fields)
45
+ # @record && @record.netzke_array(fields)
46
46
  # end
47
47
 
48
48
  def values
49
- record && record.to_hash(fields)
49
+ record && record.netzke_hash(fields)
50
50
  end
51
51
 
52
52
  # Implementation for the "netzke_submit" endpoint (for backward compatibility)
53
53
  def netzke_submit(params)
54
54
  data = ActiveSupport::JSON.decode(params[:data])
55
+ data.each_pair do |k,v|
56
+ data[k]=nil if v.blank? || v == "null" # Ext JS returns "null" on empty date fields, or "" for not filled optional integer fields, which gives errors when passed to model (at least in DataMapper)
57
+ end
55
58
 
56
59
  # File uploads are in raw params instead of "data" hash, so, mix them in into "data"
57
60
  if config[:file_upload]
@@ -66,8 +69,8 @@ module Netzke
66
69
  {:set_form_values => js_record_data, :set_result => true}
67
70
  else
68
71
  # flash eventual errors
69
- @record.errors.to_a.each do |msg|
70
- flash :error => msg
72
+ data_adapter.errors_array(@record).each do |error|
73
+ flash :error => error
71
74
  end
72
75
  {:netzke_feedback => @flash, :apply_form_errors => build_form_errors(record)}
73
76
  end
@@ -78,14 +81,10 @@ module Netzke
78
81
  # Builds the form errors
79
82
  def build_form_errors(record)
80
83
  form_errors = {}
81
- foreign_keys = {}
82
-
83
- # Build a hash of foreign keys and the associated model
84
- data_class.reflect_on_all_associations(:belongs_to).map{ |r|
85
- foreign_keys[r.association_foreign_key.to_sym] = r.name
86
- }
87
-
88
- record.errors.map{|field, error|
84
+ foreign_keys = data_adapter.hash_fk_model
85
+ record.errors.to_hash.map{|field, error|
86
+ # some ORM return an array for error
87
+ error = error.join ', ' if error.kind_of? Array
89
88
  # Get the correct field name for the errors on foreign keys
90
89
  if foreign_keys.has_key?(field)
91
90
  fields.each do |k, v|
@@ -93,7 +92,6 @@ module Netzke
93
92
  field = k.to_s.gsub('__', '____') if k.to_s.split('__').first == foreign_keys[field].to_s
94
93
  end
95
94
  end
96
-
97
95
  form_errors[field] ||= []
98
96
  form_errors[field] << error
99
97
  }
@@ -103,7 +101,8 @@ module Netzke
103
101
  # Creates/updates a record from hash
104
102
  def create_or_update_record(hsh)
105
103
  hsh.merge!(config[:strong_default_attrs]) if config[:strong_default_attrs]
106
- @record ||= data_class.find(:first, :conditions => {data_class.primary_key => hsh.delete(data_class.primary_key)}) # only pick up the record specified in the params if it was not provided in the configuration
104
+ @record ||= data_adapter.find_record hsh.delete(data_class.primary_key.to_s) # only pick up the record specified in the params if it was not provided in the configuration
105
+ #data_class.find(:first, :conditions => {data_class.primary_key => hsh.delete(data_class.primary_key)})
107
106
  success = true
108
107
 
109
108
  @record = data_class.new if @record.nil?
@@ -123,7 +122,7 @@ module Netzke
123
122
  #end
124
123
 
125
124
  # did we have complete success?
126
- success && @record.save
125
+ success && data_adapter.save_record(@record)
127
126
  end
128
127
 
129
128
  # API handling form load
@@ -126,6 +126,11 @@ module Netzke
126
126
  class GridPanel < Netzke::Base
127
127
  js_base_class "Ext.grid.Panel"
128
128
 
129
+ class_attribute :columns_attr
130
+
131
+ class_attribute :overridden_columns_attr
132
+ self.overridden_columns_attr = {}
133
+
129
134
  # Class-level configuration. These options directly influence the amount of generated
130
135
  # javascript code for this component's class. For example, if you don't want filters for the grid,
131
136
  # set column_filters_available to false, and the javascript for the filters won't be included at all.
@@ -198,15 +203,14 @@ module Netzke
198
203
  base.class_eval do
199
204
  class << self
200
205
  def column(name, config = {})
201
- columns = self.read_inheritable_attribute(:columns) || []
202
- columns << config.merge(:name => name.to_s)
203
- self.write_inheritable_attribute(:columns, columns)
206
+ columns = self.columns_attr || []
207
+ columns |= [config.merge(:name => name.to_s)]
208
+ self.columns_attr = columns
204
209
  end
205
210
 
206
211
  def override_column(name, config)
207
- columns = self.read_inheritable_attribute(:overridden_columns) || {}
208
- columns.merge!(name.to_sym => config)
209
- self.write_inheritable_attribute(:overridden_columns, columns)
212
+ columns = self.overridden_columns_attr.dup
213
+ self.overridden_columns_attr = columns.merge(name.to_sym => config)
210
214
  end
211
215
  end
212
216
  end
@@ -214,10 +218,10 @@ module Netzke
214
218
 
215
219
  def configuration
216
220
  super.tap do |c|
217
- c[:columns] ||= self.class.read_inheritable_attribute(:columns)
221
+ c[:columns] ||= self.columns_attr
218
222
 
219
223
  # user-passed :override_columns option should get deep_merged with the defaults
220
- c[:override_columns] = (self.class.read_inheritable_attribute(:overridden_columns) || {}).deep_merge(c[:override_columns] || {})
224
+ c[:override_columns] = self.overridden_columns_attr.deep_merge(c[:override_columns] || {})
221
225
  end
222
226
  end
223
227
 
@@ -243,8 +247,10 @@ module Netzke
243
247
 
244
248
  def get_default_association_values #:nodoc:
245
249
  columns.select{ |c| c[:name].index("__") && c[:default_value] }.each.inject({}) do |r,c|
246
- assoc, assoc_method = assoc_and_assoc_method_for_attr(c)
247
- assoc_instance = assoc.klass.find(c[:default_value])
250
+ assoc_name, assoc_method = c[:name].split '__'
251
+ assoc_class = data_adapter.class_for(assoc_name)
252
+ assoc_data_adapter = Netzke::Basepack::DataAdapters::AbstractAdapter.adapter_class(assoc_class).new(assoc_class)
253
+ assoc_instance = assoc_data_adapter.find_record c[:default_value]
248
254
  r.merge(c[:name] => assoc_instance.send(assoc_method))
249
255
  end
250
256
  end