netzke-basepack 0.12.9 → 1.0.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/CHANGELOG.md +75 -44
  2. data/Gemfile +4 -2
  3. data/LICENSE +2 -6
  4. data/README.md +22 -24
  5. data/javascripts/basepack.js +0 -8
  6. data/javascripts/{columns.js → grid/columns.js} +59 -71
  7. data/javascripts/grid/event_handlers.js +218 -0
  8. data/javascripts/netzkeremotecombo.js +5 -13
  9. data/javascripts/tristate.js +62 -0
  10. data/javascripts/xdatetime.js +8 -37
  11. data/lib/netzke-basepack.rb +3 -2
  12. data/lib/netzke/basepack.rb +1 -1
  13. data/lib/netzke/basepack/action_column.rb +6 -23
  14. data/lib/netzke/basepack/active_record.rb +0 -6
  15. data/lib/netzke/basepack/attr_config.rb +20 -11
  16. data/lib/netzke/basepack/attribute_config.rb +10 -0
  17. data/lib/netzke/basepack/attributes.rb +196 -0
  18. data/lib/netzke/basepack/column_config.rb +47 -39
  19. data/lib/netzke/basepack/columns.rb +127 -97
  20. data/lib/netzke/basepack/data_accessor.rb +7 -48
  21. data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +15 -17
  22. data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +111 -90
  23. data/lib/netzke/basepack/dynamic_tab_panel.rb +3 -5
  24. data/lib/netzke/basepack/dynamic_tab_panel/{javascripts → client}/dynamic_tab_panel.js +6 -5
  25. data/lib/netzke/basepack/field_config.rb +30 -19
  26. data/lib/netzke/basepack/fields.rb +22 -12
  27. data/lib/netzke/basepack/grid_live_search.rb +1 -4
  28. data/lib/netzke/basepack/grid_live_search/{javascripts → client}/grid_live_search.js +9 -7
  29. data/lib/netzke/basepack/item_persistence.rb +3 -3
  30. data/lib/netzke/basepack/item_persistence/events_plugin.rb +8 -10
  31. data/lib/netzke/basepack/paging_form.rb +7 -11
  32. data/lib/netzke/basepack/paging_form/{javascripts → client}/paging_form.js +4 -4
  33. data/lib/netzke/basepack/query_builder.rb +12 -10
  34. data/lib/netzke/basepack/query_builder/{javascripts → client}/query_builder.js +14 -12
  35. data/lib/netzke/basepack/record_form_window.rb +8 -8
  36. data/lib/netzke/basepack/search_panel.rb +4 -6
  37. data/lib/netzke/basepack/search_panel/{javascripts → client}/condition_field.js +13 -16
  38. data/lib/netzke/basepack/search_panel/{javascripts → client}/search_panel.js +7 -7
  39. data/lib/netzke/basepack/search_window.rb +6 -6
  40. data/lib/netzke/basepack/simple_app/{javascripts → client}/statusbar_ext.js +0 -0
  41. data/lib/netzke/basepack/version.rb +1 -1
  42. data/lib/netzke/form/base.rb +166 -0
  43. data/lib/netzke/{basepack/form/javascripts/form.js → form/base/client/base.js} +77 -38
  44. data/lib/netzke/form/base/client/readonly_mode.css +4 -0
  45. data/lib/netzke/{basepack/form/javascripts → form/base/client}/readonly_mode.js +5 -5
  46. data/lib/netzke/form/endpoints.rb +33 -0
  47. data/lib/netzke/form/services.rb +74 -0
  48. data/lib/netzke/grid/actions.rb +52 -0
  49. data/lib/netzke/grid/base.rb +289 -0
  50. data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/advanced_search.js +5 -1
  51. data/lib/netzke/{basepack/grid/javascripts/grid.js → grid/base/client/base.js} +61 -53
  52. data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/extensions.js +19 -13
  53. data/lib/netzke/{basepack/grid/javascripts → grid/base/client}/remember_selection.js +0 -1
  54. data/lib/netzke/grid/client.rb +8 -0
  55. data/lib/netzke/grid/components.rb +55 -0
  56. data/lib/netzke/grid/configuration.rb +72 -0
  57. data/lib/netzke/grid/endpoints.rb +99 -0
  58. data/lib/netzke/grid/permissions.rb +18 -0
  59. data/lib/netzke/grid/services.rb +141 -0
  60. data/lib/netzke/tree/base.rb +173 -0
  61. data/lib/netzke/{basepack/tree/javascripts/tree.js → tree/base/client/base.js} +55 -26
  62. data/lib/netzke/{basepack/tree/javascripts → tree/base/client}/extensions.js +7 -7
  63. data/lib/netzke/tree/endpoints.rb +34 -0
  64. data/lib/netzke/{basepack/viewport.rb → viewport/base.rb} +3 -3
  65. data/lib/netzke/{basepack/window.rb → window/base.rb} +7 -8
  66. data/lib/netzke/window/base/client/base.js +26 -0
  67. data/locales/de.yml +49 -33
  68. data/locales/en.yml +32 -39
  69. data/locales/es.yml +39 -25
  70. data/locales/nl.yml +39 -25
  71. data/locales/ru.yml +38 -25
  72. data/locales/uk.yml +40 -26
  73. data/stylesheets/basepack.css +10 -0
  74. metadata +48 -45
  75. data/javascripts/mixins/grid_event_handlers.js +0 -139
  76. data/lib/netzke/basepack/accordion.rb +0 -45
  77. data/lib/netzke/basepack/active_record/relation_extensions.rb +0 -27
  78. data/lib/netzke/basepack/form.rb +0 -131
  79. data/lib/netzke/basepack/form/endpoints.rb +0 -35
  80. data/lib/netzke/basepack/form/services.rb +0 -74
  81. data/lib/netzke/basepack/form/stylesheets/readonly_mode.css +0 -14
  82. data/lib/netzke/basepack/grid.rb +0 -570
  83. data/lib/netzke/basepack/grid/endpoints.rb +0 -111
  84. data/lib/netzke/basepack/grid/javascripts/edit_in_form.js +0 -51
  85. data/lib/netzke/basepack/grid/services.rb +0 -148
  86. data/lib/netzke/basepack/tab_panel.rb +0 -22
  87. data/lib/netzke/basepack/tab_panel/javascripts/tab_panel.js +0 -11
  88. data/lib/netzke/basepack/tree.rb +0 -269
  89. data/lib/netzke/basepack/window/javascripts/window.js +0 -26
  90. data/lib/netzke/basepack/wrap_lazy_loaded.rb +0 -29
@@ -2,57 +2,16 @@ module Netzke
2
2
  module Basepack
3
3
  # This module is included into such data-driven components as Grid, Form, PagingForm, etc.
4
4
  module DataAccessor
5
- # Returns options for comboboxes in grids/forms
6
- def combobox_options_for_column(column, method_options = {})
7
- data_adapter.combobox_options_for_column column, method_options
8
- end
9
-
10
- # Normalize array of attributes
11
- # [:col1, "col2", {:name => :col3}] #=> [{:name => "col1"}, {:name => "col2"}, {:name => "col3"}]
12
- def normalize_attrs(attrs)
13
- attrs.map{ |a| normalize_attr(a) }
14
- end
15
-
16
- # Normalize an attribute, e.g.:
17
- # :first_name =>
18
- # {:name => "first_name"}
19
- def normalize_attr(a)
20
- a.is_a?(Symbol) || a.is_a?(String) ? {:name => a.to_s} : a.merge(:name => a[:name].to_s)
21
- end
22
-
23
- def association_attr?(attr)
24
- !!attr[:name].to_s.index("__")
25
- end
26
-
27
- # Model class
28
- def data_class
29
- @data_class ||= config[:model].is_a?(String) ? config[:model].constantize : config[:model]
5
+ # Model class as specified in configuration. May be handy to override.
6
+ # Returns ORM model class.
7
+ def model
8
+ @model ||= config[:model].is_a?(String) ? config[:model].constantize : config[:model]
30
9
  end
31
10
 
32
11
  # Data adapter responsible for all DB-related operations.
33
- # Note that if data_class is nil, AbstractAdapter will used.
34
- def data_adapter
35
- @data_adapter ||= Netzke::Basepack::DataAdapters::AbstractAdapter.adapter_class(data_class).new(data_class)
36
- end
37
-
38
- # Mark an attribute as "virtual" by default, when it doesn't reflect a model column, or a model column of an association
39
- def set_default_virtual(c)
40
- c[:virtual] = data_adapter.virtual_attribute?(c) if c[:virtual].nil?
41
- end
42
-
43
- # Returns a hash of association attribute default values. Used when creating new records with association attributes that have a default value
44
- def default_association_values(attr_hash) #:nodoc:
45
- @_default_association_values ||= {}.tap do |values|
46
- attr_hash.each_pair do |name,c|
47
- next unless association_attr?(c) && c[:default_value]
48
-
49
- assoc_name, assoc_method = c[:name].split '__'
50
- assoc_class = data_adapter.class_for(assoc_name)
51
- assoc_data_adapter = Netzke::Basepack::DataAdapters::AbstractAdapter.adapter_class(assoc_class).new(assoc_class)
52
- assoc_instance = assoc_data_adapter.find_record c[:default_value]
53
- values[name] = assoc_instance.send(assoc_method)
54
- end
55
- end
12
+ # Note that if model is nil, AbstractAdapter will used.
13
+ def model_adapter
14
+ @model_adapter ||= Netzke::Basepack::DataAdapters::AbstractAdapter.adapter_class(model).new(model)
56
15
  end
57
16
  end
58
17
  end
@@ -1,7 +1,7 @@
1
1
  module Netzke::Basepack::DataAdapters
2
2
  # A concrete adapter should implement all the public instance methods of this adapter in order to support all the functionality of Basepack components.
3
3
  class AbstractAdapter
4
- attr_accessor :model_class
4
+ attr_accessor :model
5
5
 
6
6
  # Returns primary key name of the model
7
7
  def primary_key
@@ -18,7 +18,7 @@ module Netzke::Basepack::DataAdapters
18
18
  []
19
19
  end
20
20
 
21
- # Returns a list of model attribute hashes, each containing `name`, `attr_type` and `default_value` (if set in the schema).
21
+ # Returns a list of model attribute names.
22
22
  # For association columns the name can have the double-underscore format, e.g.: `author__first_name`.
23
23
  # These attributes will be used by grids and forms to display default columns/fields.
24
24
  def model_attributes
@@ -48,7 +48,7 @@ module Netzke::Basepack::DataAdapters
48
48
  # [start]
49
49
  # page number in pagination
50
50
  # [scope]
51
- # the scope as described in Netzke::Basepack::Grid
51
+ # the scope as described in Netzke::Grid::Base
52
52
  # [filters]
53
53
  # an array of hashes representing a filter query, where the hashes have the following keys:
54
54
  # [attr]
@@ -75,7 +75,7 @@ module Netzke::Basepack::DataAdapters
75
75
  #
76
76
  # `params` is a hash that contains the following keys:
77
77
  #
78
- # * :scope - the scope as described in Netzke::Basepack::Grid
78
+ # * :scope - the scope as described in Netzke::Grid::Base
79
79
  # * :filter - Ext filters
80
80
  #
81
81
  # The `columns` parameter may be used to use joins to address the n+1 query problem, and receives an array of column configurations
@@ -225,24 +225,22 @@ module Netzke::Basepack::DataAdapters
225
225
  name.to_s.humanize
226
226
  end
227
227
 
228
- # Returns root record for tree-like data
228
+ # Return root record for tree-like data
229
229
  def root
230
- model_class.root
230
+ model.root
231
231
  end
232
232
 
233
- # Children records for given record in the tree; extra scope (lambda/proc) is optional
234
- def find_record_children(r, scope = nil)
235
- r.children.extend_with(scope)
233
+ def find_record_children(r)
234
+ r.children
236
235
  end
237
236
 
238
- # All root records in the tree
239
- def find_root_records(scope)
240
- model_class.where(parent_id: nil).extend_with(scope)
237
+ def find_root_records
238
+ model.where(parent_id: nil)
241
239
  end
242
240
 
243
241
  # Does record respond to given method?
244
242
  def model_respond_to?(method)
245
- @model_class.instance_methods.include?(method)
243
+ @model.instance_methods.include?(method)
246
244
  end
247
245
 
248
246
  # -- End of overridable methods
@@ -261,12 +259,12 @@ module Netzke::Basepack::DataAdapters
261
259
  @subclasses << subclass
262
260
  end
263
261
 
264
- def self.adapter_class(model_class)
265
- @subclasses.detect { |subclass| subclass.for_class?(model_class) } || AbstractAdapter
262
+ def self.adapter_class(model)
263
+ @subclasses.detect { |subclass| subclass.for_class?(model) } || AbstractAdapter
266
264
  end
267
265
 
268
- def initialize(model_class)
269
- @model_class = model_class
266
+ def initialize(model)
267
+ @model = model
270
268
  end
271
269
  end
272
270
  end
@@ -1,22 +1,22 @@
1
1
  module Netzke::Basepack::DataAdapters
2
2
  # Implementation of {Netzke::Basepack::DataAdapters::AbstractAdapter}
3
3
  class ActiveRecordAdapter < AbstractAdapter
4
- def self.for_class?(model_class)
5
- model_class && model_class <= ActiveRecord::Base
4
+ def self.for_class?(model)
5
+ model && model <= ActiveRecord::Base
6
6
  end
7
7
 
8
8
  def new_record(params = {})
9
- @model_class.new(params)
9
+ @model.new(params)
10
10
  end
11
11
 
12
12
  def primary_key
13
- @model_class.primary_key.to_s
13
+ @model.primary_key.to_s
14
14
  end
15
15
 
16
16
  def model_attributes
17
- @_model_attributes ||= attribute_names.map do |column_name|
17
+ @model_attributes ||= attribute_names.map do |column_name|
18
18
  # If it's named as foreign key of some association, then it's an association column
19
- assoc = @model_class.reflect_on_all_associations.detect { |a| a.foreign_key == column_name }
19
+ assoc = @model.reflect_on_all_associations.detect { |a| a.foreign_key == column_name }
20
20
 
21
21
  if assoc && !assoc.options[:polymorphic]
22
22
  candidates = %w{name title label} << assoc.klass.primary_key
@@ -26,71 +26,29 @@ module Netzke::Basepack::DataAdapters
26
26
  column_name.to_sym
27
27
  end
28
28
  # auto set up the default value from the column settings
29
- # c[:default_value] = @model_class.columns_hash[column_name].default if @model_class.columns_hash[column_name].default
29
+ # c[:default_value] = @model.columns_hash[column_name].default if @model.columns_hash[column_name].default
30
30
  end
31
31
  end
32
32
 
33
33
  def attribute_names
34
- @model_class.column_names
35
- end
36
-
37
- def human_attribute_name(attr_name)
38
- @model_class.human_attribute_name(attr_name)
34
+ @model.column_names
39
35
  end
40
36
 
41
37
  def attr_type(attr_name)
42
38
  method, assoc = method_and_assoc(attr_name)
43
- klass = assoc.nil? ? @model_class : assoc.klass
39
+ klass = assoc.nil? ? @model : assoc.klass
44
40
  klass.columns_hash[method].try(:type) || :string
45
41
  end
46
42
 
47
43
  # Implementation for {AbstractAdapter#get_records}
48
44
  def get_records(params, columns=[])
49
- # build initial relation based on passed params
50
45
  relation = get_relation(params)
51
46
 
52
- # addressing the n+1 query problem
53
- columns.each do |c|
54
- assoc, method = c[:name].split('__')
55
- relation = relation.includes(assoc.to_sym).references(assoc.to_sym) if method
56
- end
57
-
58
- # apply sorting if needed
59
- if params[:sorters]
60
- sorters = Array.new(params[:sorters])
61
- sorters.each do |sorter|
62
- sorter["direction"] ||= 'ASC'
63
- dir = sorter["direction"].downcase
64
- column = columns.detect { |c| c[:name] == sorter["property"] }
65
- column ||= {name: sorter["property"]} # stub column, as we may want to sort by a column that's not in the grid
66
- relation = apply_sorting(relation, column, dir)
67
- end
68
- end
69
-
70
- #page = params[:limit] ? params[:start].to_i/params[:limit].to_i + 1 : 1
71
- if params[:limit]
72
- relation.offset(params[:start]).limit(params[:limit])
73
- else
74
- relation
75
- end
76
- end
77
-
78
- def apply_sorting(relation, column, dir)
79
- assoc, method = column[:name].split('__')
47
+ relation = fix_nplus1_problem(relation, columns)
80
48
 
81
- # if a sorting scope is set, call the scope with the given direction
82
- if column.has_key?(:sorting_scope)
83
- relation = relation.send(column[:sorting_scope].to_sym, dir.to_sym)
84
- else
85
- relation = if method.nil?
86
- relation.order("#{@model_class.table_name}.#{assoc} #{dir}")
87
- else
88
- assoc = @model_class.reflect_on_association(assoc.to_sym)
89
- relation.includes(assoc.name).references(assoc.klass.table_name.to_sym).order("#{assoc.klass.table_name}.#{method} #{dir}")
90
- end
91
- end
49
+ relation = apply_sorting(relation, columns, params[:sorters])
92
50
 
93
- relation
51
+ relation = apply_offset(relation, params)
94
52
  end
95
53
 
96
54
  def count_records(params, columns=[])
@@ -107,7 +65,7 @@ module Netzke::Basepack::DataAdapters
107
65
  end
108
66
 
109
67
  def get_assoc_property_type assoc_name, prop_name
110
- if prop_name && assoc = @model_class.reflect_on_association(assoc_name)
68
+ if prop_name && assoc = @model.reflect_on_association(assoc_name)
111
69
  assoc_column = assoc.klass.columns_hash[prop_name.to_s]
112
70
  assoc_column.try(:type)
113
71
  end
@@ -120,7 +78,7 @@ module Netzke::Basepack::DataAdapters
120
78
  if assoc
121
79
  return !assoc.klass.column_names.include?(method)
122
80
  else
123
- return !@model_class.column_names.include?(c[:name])
81
+ return !@model.column_names.include?(c[:name])
124
82
  end
125
83
  end
126
84
 
@@ -131,9 +89,16 @@ module Netzke::Basepack::DataAdapters
131
89
  # Options for an asssociation attribute
132
90
 
133
91
  relation = assoc.klass.all
134
- relation = relation.extend_with(attr[:scope]) if attr[:scope]
92
+ relation = attr[:scope].call(relation) if attr[:scope].is_a?(Proc)
135
93
 
136
- if assoc.klass.column_names.include?(method)
94
+ if attr[:filter_association_with]
95
+ relation = attr[:filter_association_with].call(relation, query).to_a
96
+ if attr[:getter]
97
+ relation.map{ |r| [r.id, attr[:getter].call(r)] }
98
+ else
99
+ relation.map{ |r| [r.id, r.send(method)] }
100
+ end
101
+ elsif assoc.klass.column_names.include?(method)
137
102
  # apply query
138
103
  assoc_arel_table = assoc.klass.arel_table
139
104
 
@@ -151,35 +116,37 @@ module Netzke::Basepack::DataAdapters
151
116
  end
152
117
 
153
118
  def distinct_combo_values(attr, query)
154
- records = query.empty? ? @model_class.find_by_sql("select distinct #{attr[:name]} from #{@model_class.table_name}") : @model_class.find_by_sql("select distinct #{attr[:name]} from #{@model_class.table_name} where #{attr[:name]} like '#{query}%'")
119
+ records = query.empty? ? @model.find_by_sql("select distinct #{attr[:name]} from #{@model.table_name}") : @model.find_by_sql("select distinct #{attr[:name]} from #{@model.table_name} where #{attr[:name]} like '#{query}%'")
155
120
  records.map{|r| [r.send(attr[:name]), r.send(attr[:name])]}
156
121
  end
157
122
 
158
123
  def foreign_key_for assoc_name
159
- @model_class.reflect_on_association(assoc_name.to_sym).foreign_key
124
+ @model.reflect_on_association(assoc_name.to_sym).foreign_key
160
125
  end
161
126
 
162
127
  # Returns the model class for association columns
163
128
  def class_for assoc_name
164
- @model_class.reflect_on_association(assoc_name.to_sym).klass
129
+ @model.reflect_on_association(assoc_name.to_sym).klass
165
130
  end
166
131
 
167
132
  def destroy(ids)
168
- @model_class.destroy(ids)
133
+ @model.destroy(ids)
169
134
  end
170
135
 
171
136
  # Returns a record by id.
172
137
  # Respects the following options:
173
138
  # * scope - will only return a record if it falls into the provided scope
174
139
  def find_record(id, options = {})
175
- scope = options[:scope] || {}
176
- @model_class.where(primary_key => id).extend_with(scope).first
140
+ # scope = options[:scope] || {}
141
+ relation = @model.where(primary_key => id)
142
+ relation = options[:scope].call(relation) if options[:scope].is_a?(Proc)
143
+ relation.first
177
144
  end
178
145
 
179
146
  # Build a hash of foreign keys and the associated model
180
147
  def hash_fk_model
181
148
  foreign_keys = {}
182
- @model_class.reflect_on_all_associations(:belongs_to).map{ |r|
149
+ @model.reflect_on_all_associations(:belongs_to).map{ |r|
183
150
  foreign_keys[r.association_foreign_key.to_sym] = r.name
184
151
  }
185
152
  foreign_keys
@@ -187,10 +154,10 @@ module Netzke::Basepack::DataAdapters
187
154
 
188
155
  # FIXME
189
156
  def move_records(params)
190
- if defined?(ActsAsList) && @model_class.ancestors.include?(ActsAsList::InstanceMethods)
157
+ if defined?(ActsAsList) && @model.ancestors.include?(ActsAsList::InstanceMethods)
191
158
  ids = JSON.parse(params[:ids]).reverse
192
159
  ids.each_with_index do |id, i|
193
- r = @model_class.find(id)
160
+ r = @model.find(id)
194
161
  r.insert_at(params[:new_index].to_i + i + 1)
195
162
  end
196
163
  on_data_changed # copypaste nonsense
@@ -216,21 +183,21 @@ module Netzke::Basepack::DataAdapters
216
183
  end
217
184
 
218
185
  def human_attribute_name(name)
219
- @model_class.human_attribute_name(name)
186
+ @model.human_attribute_name(name)
220
187
  end
221
188
 
222
189
  def record_value_for_attribute(r, a, through_association = false)
223
- v = if a[:getter]
224
- a[:getter].call(r)
225
- elsif r.respond_to?("#{a[:name]}")
226
- r.send("#{a[:name]}")
227
- elsif association_attr?(a)
190
+ v = if association_attr?(a)
228
191
  split = a[:name].to_s.split(/\.|__/)
229
- assoc = @model_class.reflect_on_association(split.first.to_sym)
192
+ assoc = @model.reflect_on_association(split.first.to_sym)
230
193
  if through_association
231
- split.inject(r) do |r,m| # Do we *really* need to descend deeper than 1 level?
194
+ split.inject(r) do |r, m| # Do we *really* need to descend deeper than 1 level?
232
195
  return nil if r.nil?
233
- if r.respond_to?(m)
196
+
197
+ # On the last iteration call the getter block
198
+ if a[:getter] && split.last.equal?(m)
199
+ a[:getter].call(r)
200
+ elsif r.respond_to?(m)
234
201
  r.send(m)
235
202
  else
236
203
  logger.warn "Netzke: Wrong attribute name: #{a[:name]}" unless r.nil?
@@ -240,6 +207,11 @@ module Netzke::Basepack::DataAdapters
240
207
  else
241
208
  r.send("#{assoc.options[:foreign_key] || assoc.name.to_s.foreign_key}")
242
209
  end
210
+ elsif a[:getter]
211
+ a[:getter].call(r)
212
+ elsif r.respond_to?("#{a[:name]}")
213
+ r.send("#{a[:name]}")
214
+
243
215
  # the composite_primary_keys gem produces [Key1,Key2...] and [Value1,Value2...]
244
216
  # on primary_key and id requests. Basepack::AttrConfig converts the keys-array to an String.
245
217
  elsif primary_key.try(:to_s) == a[:name]
@@ -253,13 +225,11 @@ module Netzke::Basepack::DataAdapters
253
225
  end
254
226
 
255
227
  def set_record_value_for_attribute(record, attr, value)
256
- ::Rails.logger.debug "\n!!! attr: #{attr.inspect}\n"
257
228
  value = value.to_time_in_current_zone if value.is_a?(Date) # convert Date to Time
258
229
  unless attr[:read_only]
259
230
  if attr[:setter]
260
231
  attr[:setter].call(record, value)
261
232
  elsif record.respond_to?("#{attr[:name]}=")
262
- ::Rails.logger.debug "\n!!! value: #{value.inspect}\n"
263
233
  record.send("#{attr[:name]}=", value)
264
234
  elsif association_attr?(attr)
265
235
  split = attr[:name].to_s.split(/\.|__/)
@@ -268,11 +238,11 @@ module Netzke::Basepack::DataAdapters
268
238
  # set_value_for_attribute({:name => :assoc_1__assoc_2__method, :nested_attribute => true}, 100)
269
239
  # =>
270
240
  # record.assoc_1.assoc_2.method = 100
271
- split.inject(record) { |r,m| m == split.last ? (r && r.send("#{m}=", v) && r.save) : r.send(m) }
241
+ split.inject(record) { |r,m| m == split.last ? (r && r.send("#{m}=", value) && r.save) : r.send(m) }
272
242
  else
273
243
  if split.size == 2
274
244
  # search for association and assign it to r
275
- assoc = @model_class.reflect_on_association(split.first.to_sym)
245
+ assoc = @model.reflect_on_association(split.first.to_sym)
276
246
  assoc_method = split.last
277
247
  if assoc
278
248
  if assoc.macro == :has_one
@@ -290,7 +260,7 @@ module Netzke::Basepack::DataAdapters
290
260
  record.send("#{assoc.foreign_key}=", value.to_i < 0 ? nil : value)
291
261
  end
292
262
  else
293
- logger.warn "Netzke: Association #{assoc} is not known for class #{@data_class}"
263
+ logger.warn "Netzke: Association #{assoc} is not known for class #{@model}"
294
264
  end
295
265
  else
296
266
  logger.warn "Netzke: Wrong attribute name: #{attr[:name]}"
@@ -304,13 +274,13 @@ module Netzke::Basepack::DataAdapters
304
274
  # Else returns [attr_name]
305
275
  def method_and_assoc(attr_name)
306
276
  assoc_name, method = attr_name.to_s.split('__')
307
- assoc = @model_class.reflect_on_association(assoc_name.to_sym) if method
277
+ assoc = @model.reflect_on_association(assoc_name.to_sym) if method
308
278
  assoc.nil? ? [attr_name] : [method, assoc]
309
279
  end
310
280
 
311
281
  # An ActiveRecord::Relation instance encapsulating all the necessary conditions.
312
282
  def get_relation(params = {})
313
- relation = @model_class.all
283
+ relation = @model.all
314
284
 
315
285
  query = params[:query]
316
286
 
@@ -337,17 +307,20 @@ module Netzke::Basepack::DataAdapters
337
307
  if params[:filters]
338
308
  and_query = params[:filters]
339
309
  and_query.each do |q|
340
- if prok = q.delete(:proc)
341
- relation = prok.call(relation, q[:value], q[:operator])
342
- and_query.delete(q)
343
- end
310
+ relation = q[:proc].call(relation, q[:value], q[:operator]) if q[:proc]
344
311
  end
345
312
 
313
+ and_query.delete_if{|q| q[:proc] }
314
+
346
315
  # apply other, non-Proc filters
347
316
  relation = relation.where(predicates_for_and_conditions(and_query))
348
317
  end
349
318
 
350
- relation = relation.extend_with(params[:scope]) if params[:scope]
319
+ if params[:scope].is_a?(Proc)
320
+ relation = params[:scope].call(relation)
321
+ else
322
+ raise ArgumentError, "Expected scope to be a Proc, got #{params[:scope].class}" unless params[:scope].nil?
323
+ end
351
324
 
352
325
  @relation = relation
353
326
  end
@@ -361,7 +334,7 @@ module Netzke::Basepack::DataAdapters
361
334
  attr = q[:attr]
362
335
  method, assoc = method_and_assoc(attr)
363
336
 
364
- arel_table = assoc ? Arel::Table.new(assoc.klass.table_name.to_sym) : @model_class.arel_table
337
+ arel_table = assoc ? Arel::Table.new(assoc.klass.table_name.to_sym) : @model.arel_table
365
338
 
366
339
  value = q["value"]
367
340
  op = q["operator"]
@@ -420,7 +393,55 @@ module Netzke::Basepack::DataAdapters
420
393
  end
421
394
  end
422
395
 
423
- private
396
+ protected
397
+
398
+ # Addresses the n+1 query problem
399
+ # Returns updated relation
400
+ def fix_nplus1_problem(relation, columns)
401
+ columns.reduce(relation) do |rel, c|
402
+ assoc, method = c[:name].split('__')
403
+ method ? rel.includes(assoc.to_sym).references(assoc.to_sym) : rel
404
+ end
405
+ end
406
+
407
+ def apply_sorting(relation, columns, sorters)
408
+ return relation if sorters.blank?
409
+
410
+ sorters = Array.new(sorters)
411
+
412
+ relation = relation.reorder("") # reset eventual default_scope ordering
413
+
414
+ sorters.reduce(relation) do |rel, sorter|
415
+ sorter["direction"] ||= 'ASC'
416
+ dir = sorter["direction"].downcase
417
+ column = columns.detect { |c| c[:name] == sorter["property"] }
418
+ column ||= {name: sorter["property"]} # stub column, as we may want to sort by a column that's not in the grid
419
+ apply_column_sorting(rel, column, dir)
420
+ end
421
+ end
422
+
423
+ def apply_column_sorting(relation, column, dir)
424
+ assoc, method = column[:name].split('__')
425
+
426
+ # if a sorting scope is set, call the scope with the given direction
427
+ if column[:sorting_scope].is_a?(Proc)
428
+ column[:sorting_scope].call(relation, dir.to_sym)
429
+ else
430
+ if method.nil?
431
+ relation.order("#{@model.table_name}.#{assoc} #{dir}")
432
+ else
433
+ assoc = @model.reflect_on_association(assoc.to_sym)
434
+ relation.includes(assoc.name).references(assoc.klass.table_name.to_sym).order("#{assoc.klass.table_name}.#{method} #{dir}")
435
+ end
436
+ end
437
+ end
438
+
439
+ def apply_offset(relation, params)
440
+ return relation if params[:limit].blank?
441
+ relation.offset(params[:start]).limit(params[:limit])
442
+ end
443
+
444
+ private
424
445
 
425
446
  def logger
426
447
  Netzke::Base.logger