netzke-basepack 0.7.7 → 0.8.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.
Files changed (192) hide show
  1. data/.travis.yml +15 -10
  2. data/{CHANGELOG.rdoc → CHANGELOG.md} +146 -110
  3. data/LICENSE +7 -1
  4. data/README.md +47 -56
  5. data/Rakefile +5 -5
  6. data/config/before-travis.sh +10 -0
  7. data/javascripts/basepack.js +0 -130
  8. data/javascripts/netzkeremotecombo.js +59 -0
  9. data/lib/netzke/basepack.rb +9 -14
  10. data/lib/netzke/basepack/accordion.rb +45 -0
  11. data/lib/netzke/basepack/active_record.rb +12 -0
  12. data/lib/netzke/basepack/active_record/relation_extensions.rb +27 -0
  13. data/lib/netzke/basepack/columns.rb +309 -0
  14. data/lib/netzke/basepack/data_accessor.rb +22 -12
  15. data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +75 -11
  16. data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +154 -49
  17. data/lib/netzke/basepack/fields.rb +162 -0
  18. data/lib/netzke/basepack/form.rb +136 -0
  19. data/lib/netzke/basepack/{form_panel → form}/javascripts/comma_list_cbg.js +0 -1
  20. data/lib/netzke/basepack/{form_panel/javascripts/form_panel.js → form/javascripts/form.js} +20 -26
  21. data/lib/netzke/basepack/{form_panel → form}/javascripts/n_radio_group.js +0 -1
  22. data/lib/netzke/basepack/{form_panel → form}/javascripts/readonly_mode.js +0 -0
  23. data/lib/netzke/basepack/form/services.rb +115 -0
  24. data/lib/netzke/basepack/{form_panel → form}/stylesheets/readonly_mode.css +0 -0
  25. data/lib/netzke/basepack/grid.rb +355 -0
  26. data/lib/netzke/basepack/{grid_panel → grid}/javascripts/advanced_search.js +1 -1
  27. data/lib/netzke/basepack/{grid_panel → grid}/javascripts/check_column_fix.js +0 -0
  28. data/lib/netzke/basepack/{grid_panel → grid}/javascripts/edit_in_form.js +3 -3
  29. data/lib/netzke/basepack/{grid_panel → grid}/javascripts/event_handling.js +5 -2
  30. data/lib/netzke/basepack/{grid_panel/javascripts/grid_panel.js → grid/javascripts/grid.js} +120 -132
  31. data/lib/netzke/basepack/{grid_panel → grid}/javascripts/misc.js +0 -0
  32. data/lib/netzke/basepack/grid/services.rb +216 -0
  33. data/lib/netzke/basepack/item_persistence.rb +44 -0
  34. data/lib/netzke/basepack/item_persistence/events_plugin.rb +47 -0
  35. data/lib/netzke/basepack/{paging_form_panel.rb → paging_form.rb} +24 -30
  36. data/lib/netzke/basepack/{paging_form_panel/javascripts/paging_form_panel.js → paging_form/javascripts/paging_form.js} +2 -4
  37. data/lib/netzke/basepack/query_builder.rb +44 -73
  38. data/lib/netzke/basepack/query_builder/javascripts/query_builder.js +16 -2
  39. data/lib/netzke/basepack/record_form_window.rb +67 -0
  40. data/lib/netzke/basepack/search_panel.rb +22 -24
  41. data/lib/netzke/basepack/search_panel/javascripts/condition_field.js +2 -2
  42. data/lib/netzke/basepack/search_window.rb +47 -53
  43. data/lib/netzke/basepack/simple_app.rb +10 -13
  44. data/lib/netzke/basepack/simple_app/javascripts/simple_app.js +2 -8
  45. data/lib/netzke/basepack/tab_panel.rb +5 -4
  46. data/lib/netzke/basepack/tab_panel/javascripts/tab_panel.js +5 -5
  47. data/lib/netzke/basepack/version.rb +2 -2
  48. data/lib/netzke/basepack/viewport.rb +16 -0
  49. data/lib/netzke/basepack/window.rb +27 -18
  50. data/lib/netzke/basepack/window/javascripts/window.js +7 -1
  51. data/lib/netzke/basepack/wrap_lazy_loaded.rb +18 -18
  52. data/locales/en.yml +40 -24
  53. data/netzke-basepack.gemspec +51 -82
  54. data/stylesheets/basepack.css +0 -41
  55. data/test/basepack_test_app/Gemfile +9 -46
  56. data/test/basepack_test_app/Gemfile.lock +61 -96
  57. data/test/basepack_test_app/app/components/author_form.rb +8 -5
  58. data/test/basepack_test_app/app/components/author_grid.rb +2 -2
  59. data/test/basepack_test_app/app/components/book_form.rb +34 -31
  60. data/test/basepack_test_app/app/components/book_form_with_defaults.rb +6 -7
  61. data/test/basepack_test_app/app/components/book_form_with_file_upload.rb +10 -0
  62. data/test/basepack_test_app/app/components/book_form_with_nested_attributes.rb +5 -6
  63. data/test/basepack_test_app/app/components/book_grid.rb +19 -8
  64. data/test/basepack_test_app/app/components/book_grid_filtering.rb +4 -7
  65. data/test/basepack_test_app/app/components/book_grid_loader.rb +28 -15
  66. data/test/basepack_test_app/app/components/book_grid_with_custom_columns.rb +45 -21
  67. data/test/basepack_test_app/app/components/book_grid_with_default_values.rb +26 -8
  68. data/test/basepack_test_app/app/components/book_grid_with_excluded_columns.rb +11 -0
  69. data/test/basepack_test_app/app/components/book_grid_with_extra_feedback.rb +2 -2
  70. data/test/basepack_test_app/app/components/book_grid_with_extra_filters.rb +7 -6
  71. data/test/basepack_test_app/app/components/book_grid_with_mass_assignment_security.rb +9 -0
  72. data/test/basepack_test_app/app/components/book_grid_with_nested_attributes.rb +9 -9
  73. data/test/basepack_test_app/app/components/book_grid_with_overridden_columns.rb +5 -3
  74. data/test/basepack_test_app/app/components/book_grid_with_paging.rb +6 -8
  75. data/test/basepack_test_app/app/components/book_grid_with_persistence.rb +6 -4
  76. data/test/basepack_test_app/app/components/book_grid_with_scope.rb +6 -0
  77. data/test/basepack_test_app/app/components/book_grid_with_scoped_authors.rb +10 -7
  78. data/test/basepack_test_app/app/components/book_grid_with_virtual_attributes.rb +21 -13
  79. data/test/basepack_test_app/app/components/book_paging_form.rb +21 -0
  80. data/test/basepack_test_app/app/components/book_query_builder.rb +7 -6
  81. data/test/basepack_test_app/app/components/book_with_custom_primary_key_grid.rb +6 -7
  82. data/test/basepack_test_app/app/components/books_bound_to_author.rb +9 -7
  83. data/test/basepack_test_app/app/components/border_layout_panel_with_persistence.rb +12 -0
  84. data/test/basepack_test_app/app/components/double_book_grid.rb +19 -14
  85. data/test/basepack_test_app/app/components/form_without_model.rb +15 -16
  86. data/test/basepack_test_app/app/components/grid_with_initial_sorting.rb +7 -0
  87. data/test/basepack_test_app/app/components/grid_with_inline_data.rb +7 -0
  88. data/test/basepack_test_app/app/components/paging_form_with_search.rb +2 -2
  89. data/test/basepack_test_app/app/components/panel_with_persistent_regions.rb +35 -0
  90. data/test/basepack_test_app/app/components/query_builder.rb +7 -0
  91. data/test/basepack_test_app/app/components/simple_panel.rb +16 -11
  92. data/test/basepack_test_app/app/components/simple_window.rb +7 -6
  93. data/test/basepack_test_app/app/components/some_accordion.rb +18 -0
  94. data/test/basepack_test_app/app/components/some_auth_app.rb +5 -5
  95. data/test/basepack_test_app/app/components/some_border_layout.rb +20 -20
  96. data/test/basepack_test_app/app/components/some_search_panel.rb +6 -0
  97. data/test/basepack_test_app/app/components/some_simple_app.rb +30 -16
  98. data/test/basepack_test_app/app/components/some_tab_panel.rb +18 -15
  99. data/test/basepack_test_app/app/components/user_form.rb +18 -16
  100. data/test/basepack_test_app/app/components/user_form_with_default_fields.rb +5 -6
  101. data/test/basepack_test_app/app/components/user_grid.rb +11 -6
  102. data/test/basepack_test_app/app/components/user_grid_with_customized_form_fields.rb +5 -3
  103. data/test/basepack_test_app/app/components/window_component_loader.rb +25 -21
  104. data/test/basepack_test_app/app/models/address.rb +0 -26
  105. data/test/basepack_test_app/app/models/author.rb +0 -31
  106. data/test/basepack_test_app/app/models/book.rb +1 -42
  107. data/test/basepack_test_app/app/models/book_with_custom_primary_key.rb +1 -23
  108. data/test/basepack_test_app/app/models/role.rb +0 -21
  109. data/test/basepack_test_app/app/models/user.rb +0 -24
  110. data/test/basepack_test_app/app/views/layouts/components.html.erb +1 -1
  111. data/test/basepack_test_app/config/application.rb +1 -1
  112. data/test/basepack_test_app/config/database.yml.travis +2 -6
  113. data/test/basepack_test_app/config/initializers/netzke.rb +1 -6
  114. data/test/basepack_test_app/db/schema.rb +14 -14
  115. data/test/basepack_test_app/features/accordion_panel.feature +2 -2
  116. data/test/basepack_test_app/features/form_panel.feature +7 -7
  117. data/test/basepack_test_app/features/grid_panel.feature +93 -39
  118. data/test/basepack_test_app/features/grid_panel_with_custom_primary_key.feature +2 -1
  119. data/test/basepack_test_app/features/grid_sorting.feature +30 -6
  120. data/test/basepack_test_app/features/paging_form_panel.feature +7 -7
  121. data/test/basepack_test_app/features/persistent_regions.feature +30 -0
  122. data/test/basepack_test_app/features/search_in_grid.feature +5 -5
  123. data/test/basepack_test_app/features/simple_app.feature +6 -7
  124. data/test/basepack_test_app/features/step_definitions/form_panel_steps.rb +1 -1
  125. data/test/basepack_test_app/features/step_definitions/generic_steps.rb +109 -4
  126. data/test/basepack_test_app/features/step_definitions/grid_panel_steps.rb +8 -10
  127. data/test/basepack_test_app/features/step_definitions/window_steps.rb +27 -0
  128. data/test/basepack_test_app/features/tab_panel.feature +1 -1
  129. data/test/basepack_test_app/features/window.feature +17 -0
  130. data/test/unit/accordion_panel_test.rb +2 -2
  131. data/test/unit/grid_panel_test.rb +4 -4
  132. metadata +57 -83
  133. data/TODO.rdoc +0 -8
  134. data/lib/generators/netzke/basepack_generator.rb +0 -10
  135. data/lib/generators/netzke/templates/assets/ts-checkbox.gif +0 -0
  136. data/lib/generators/netzke/templates/create_netzke_field_lists.rb +0 -18
  137. data/lib/netzke/active_record.rb +0 -20
  138. data/lib/netzke/active_record/attributes.rb +0 -259
  139. data/lib/netzke/active_record/combobox_options.rb +0 -16
  140. data/lib/netzke/active_record/relation_extensions.rb +0 -37
  141. data/lib/netzke/basepack/accordion_panel.rb +0 -39
  142. data/lib/netzke/basepack/action_column.rb +0 -68
  143. data/lib/netzke/basepack/action_column/javascripts/action_column.js +0 -61
  144. data/lib/netzke/basepack/auth_app.rb +0 -159
  145. data/lib/netzke/basepack/basic_app.rb +0 -7
  146. data/lib/netzke/basepack/border_layout_panel.rb +0 -53
  147. data/lib/netzke/basepack/border_layout_panel/javascripts/border_layout_panel.js +0 -40
  148. data/lib/netzke/basepack/data_adapters/data_mapper_adapter.rb +0 -264
  149. data/lib/netzke/basepack/data_adapters/sequel_adapter.rb +0 -260
  150. data/lib/netzke/basepack/form_panel.rb +0 -144
  151. data/lib/netzke/basepack/form_panel/fields.rb +0 -208
  152. data/lib/netzke/basepack/form_panel/javascripts/misc.js +0 -4
  153. data/lib/netzke/basepack/form_panel/services.rb +0 -142
  154. data/lib/netzke/basepack/grid_panel.rb +0 -441
  155. data/lib/netzke/basepack/grid_panel/columns.rb +0 -400
  156. data/lib/netzke/basepack/grid_panel/javascripts/rows-dd.js +0 -281
  157. data/lib/netzke/basepack/grid_panel/record_form_window.rb +0 -41
  158. data/lib/netzke/basepack/grid_panel/services.rb +0 -235
  159. data/lib/netzke/basepack/panel.rb +0 -11
  160. data/lib/netzke/basepack/wrapper.rb +0 -28
  161. data/lib/netzke/data_mapper.rb +0 -18
  162. data/lib/netzke/data_mapper/attributes.rb +0 -273
  163. data/lib/netzke/data_mapper/combobox_options.rb +0 -11
  164. data/lib/netzke/data_mapper/relation_extensions.rb +0 -38
  165. data/lib/netzke/sequel.rb +0 -18
  166. data/lib/netzke/sequel/attributes.rb +0 -274
  167. data/lib/netzke/sequel/combobox_options.rb +0 -10
  168. data/lib/netzke/sequel/relation_extensions.rb +0 -40
  169. data/locales/zh-cn.yml +0 -79
  170. data/test/basepack_test_app/app/components/book_form_with_custom_fields.rb +0 -21
  171. data/test/basepack_test_app/app/components/book_grid_with_column_actions.rb +0 -15
  172. data/test/basepack_test_app/app/components/book_grid_with_defaults.rb +0 -6
  173. data/test/basepack_test_app/app/components/book_paging_form_panel.rb +0 -22
  174. data/test/basepack_test_app/app/components/generic_user_form.rb +0 -12
  175. data/test/basepack_test_app/app/components/simple_accordion.rb +0 -11
  176. data/test/basepack_test_app/app/components/simple_tab_panel.rb +0 -11
  177. data/test/basepack_test_app/app/components/simple_wrapper.rb +0 -7
  178. data/test/basepack_test_app/app/components/some_accordion_panel.rb +0 -22
  179. data/test/basepack_test_app/app/presenters/forms/generic_user.rb +0 -6
  180. data/test/basepack_test_app/app/views/components/loadable_window.html.erb +0 -9
  181. data/test/basepack_test_app/app/views/components/simple_panel.html.erb +0 -1
  182. data/test/basepack_test_app/features/components_in_view.feature +0 -11
  183. data/test/basepack_test_app/features/simple_panel.feature +0 -11
  184. data/test/basepack_test_app/features/validations_in_grid.feature +0 -13
  185. data/test/basepack_test_app/features/virtual_attributes.feature +0 -16
  186. data/test/basepack_test_app/spec/components/form_panel_spec.rb +0 -53
  187. data/test/basepack_test_app/spec/components/grid_panel_spec.rb +0 -10
  188. data/test/basepack_test_app/spec/data_adapter/adapter_spec.rb +0 -68
  189. data/test/basepack_test_app/spec/data_adapter/attributes_spec.rb +0 -56
  190. data/test/basepack_test_app/spec/data_adapter/relation_extensions_spec.rb +0 -125
  191. data/test/basepack_test_app/spec/factories.rb +0 -28
  192. data/test/basepack_test_app/spec/spec_helper.rb +0 -39
@@ -0,0 +1,12 @@
1
+ require 'netzke/basepack/active_record/relation_extensions'
2
+
3
+ module Netzke
4
+ module Basepack
5
+ module ActiveRecord
6
+ end
7
+ end
8
+ end
9
+
10
+ ::ActiveRecord::Relation.class_eval do
11
+ include ::Netzke::Basepack::ActiveRecord::RelationExtensions
12
+ end
@@ -0,0 +1,27 @@
1
+ module Netzke
2
+ module Basepack
3
+ module ActiveRecord
4
+ module RelationExtensions
5
+ def extend_with(*params)
6
+ scope = params.shift
7
+ case scope.class.name
8
+ when "Symbol" # model's scope
9
+ self.send(scope, *params)
10
+ when "String" # SQL query or SQL query with params (e.g. ["created_at < ?", 1.day.ago])
11
+ params.empty? ? self.where(scope) : self.where([scope, *params])
12
+ when "Array"
13
+ self.extend_with(*scope)
14
+ when "Hash" # conditions hash
15
+ self.where(scope)
16
+ when "ActiveSupport::HashWithIndifferentAccess" # conditions hash
17
+ self.where(scope)
18
+ when "Proc" # receives a relation, must return a relation
19
+ scope.call(self)
20
+ else
21
+ raise ArgumentError, "Wrong parameter type for ActiveRecord::Relation#extend_with"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,309 @@
1
+ module Netzke
2
+ module Basepack
3
+ module Columns
4
+ extend ActiveSupport::Concern
5
+
6
+ COLUMN_METHOD_NAME = "%s_column"
7
+
8
+ module ClassMethods
9
+ # Overrides a column config, e.g.:
10
+ #
11
+ # column :title do |c|
12
+ # c.flex = 1
13
+ # end
14
+ def column(name, &block)
15
+ method_name = COLUMN_METHOD_NAME % name
16
+ define_method(method_name, &block)
17
+ end
18
+ end
19
+
20
+ # Returns the list of (non-normalized) columns to be used. By default returns the list of model column names.
21
+ # Can be overridden.
22
+ def columns
23
+ config.columns || data_adapter.model_attributes
24
+ end
25
+
26
+ # An array of complete columns configs ready to be passed to the JS side.
27
+ # The +options+ hash can have the following keys:
28
+ # * :with_excluded - when true, include the columns that are marked as excluded
29
+ # * :with_meta - when true, include the meta column
30
+ def final_columns(options = {})
31
+ @_final_columns ||= {}
32
+ @_final_columns[options] ||= [].tap do |cols|
33
+ initial_columns(true).each do |c|
34
+ name = c.name
35
+
36
+ # merge with column declaration
37
+ send(:"#{name}_column", c) if respond_to?(:"#{name}_column")
38
+
39
+ # set the defaults as lowest priority
40
+ augment_column_config(c)
41
+
42
+ cols << c if options[:with_excluded] || !c.excluded
43
+ end
44
+
45
+ append_meta_column(cols) if options[:with_meta]
46
+ end
47
+ end
48
+
49
+ # Columns as a hash, for easier access to a specific column
50
+ def final_columns_hash
51
+ @_final_columns_hash ||= final_columns.inject({}){|r,c| r.merge(c[:name].to_sym => c)}
52
+ end
53
+
54
+ # Columns from config.columns or the `columns` method, after normalization
55
+ def initial_columns(with_excluded = false)
56
+ @_initial_columns ||= {}
57
+ @_initial_columns[with_excluded] ||= [].tap do |cols|
58
+ has_primary_column = false
59
+
60
+ columns.each do |c|
61
+ # normalize:
62
+ # * :title => {name: 'title'}
63
+ # * {name: :some_column} => {name: 'some_column'}
64
+ c = ActiveSupport::OrderedOptions.new.replace(c.is_a?(Symbol) ? {name: c.to_s} : c.merge(name: c[:name].to_s))
65
+
66
+ cols << c if with_excluded || !c.excluded
67
+
68
+ # detect primary key column
69
+ has_primary_column ||= c.name == data_adapter.primary_key_name
70
+ end
71
+
72
+ # automatically add a column that reflects the primary key
73
+ cols.insert(0, ActiveSupport::OrderedOptions.new.replace(:name => data_adapter.primary_key_name)) unless has_primary_column
74
+ end
75
+ end
76
+
77
+ def append_meta_column(cols)
78
+ cols << {}.tap do |c|
79
+ c.merge!(
80
+ :name => "meta",
81
+ :meta => true,
82
+ :getter => lambda do |r|
83
+ meta_data(r)
84
+ end
85
+ )
86
+ c[:default_value] = meta_default_data if meta_default_data.present?
87
+ end
88
+ end
89
+
90
+ # default_value for the meta column; used when a new record is being created in the grid
91
+ def meta_default_data
92
+ default_association_values(final_columns_hash).present? ? { :association_values => default_association_values(final_columns_hash).literalize_keys } : {}
93
+ end
94
+
95
+ # Override it when you need extra meta data to be passed through the meta column
96
+ def meta_data(r)
97
+ { :association_values => data_adapter.assoc_values(r, final_columns_hash).literalize_keys }
98
+ end
99
+
100
+ private
101
+
102
+ # Based on initial column config, e.g.:
103
+ #
104
+ # {:name=>"author__name", :attr_type=>:string}
105
+ #
106
+ # augment it with additional configuration params, e.g.:
107
+ #
108
+ # {:name=>"author__name", :attr_type=>:string, :editor=>{:xtype=>:netzkeremotecombo}, :assoc=>true, :virtual=>true, :header=>"Author name", :editable=>true, :sortable=>false, :filterable=>false}
109
+ #
110
+ # It may be handy to override it.
111
+ def augment_column_config(c)
112
+ set_default_attr_type(c)
113
+ set_default_xtype(c)
114
+ set_default_virtual(c)
115
+ set_default_text(c)
116
+ set_default_editable(c)
117
+ set_default_editor(c)
118
+ set_default_width(c)
119
+ set_default_hidden(c)
120
+ set_default_sortable(c)
121
+ set_default_filterable(c)
122
+ c[:assoc] = association_attr?(c) # needed on the JS side
123
+ end
124
+
125
+ def set_default_attr_type(c)
126
+ c[:attr_type] ||= association_attr?(c) ? :integer : data_adapter.attr_type(c.name)
127
+ end
128
+
129
+ def set_default_xtype(c)
130
+ return if c[:renderer] || c[:editor] # if user set those manually, we don't mess with column xtype
131
+ c[:xtype] ||= attr_type_to_xtype_map[c[:attr_type]]
132
+ end
133
+
134
+ def set_default_text(c)
135
+ c[:text] ||= c[:label] || data_adapter.human_attribute_name(c[:name])
136
+ end
137
+
138
+ def set_default_editor(c)
139
+ # if shouldn't be editable, don't set any default editor; also, specifying xtype takes care of the editor
140
+ return if c[:read_only] || c[:editable] == false
141
+
142
+ if association_attr?(c)
143
+ set_default_association_editor(c)
144
+ else
145
+ c[:editor] ||= editor_for_attr_type(c[:attr_type])
146
+ end
147
+
148
+ end
149
+
150
+ def set_default_width(c)
151
+ c[:width] ||= 50 if c[:attr_type] == :boolean
152
+ c[:width] ||= 150 if c[:attr_type] == :datetime
153
+ end
154
+
155
+ def set_default_hidden(c)
156
+ c[:hidden] = true if data_adapter.primary_key_attr?(c) && c[:hidden].nil?
157
+ end
158
+
159
+ def set_default_editable(c)
160
+ if c[:editable].nil?
161
+ c[:editable] = is_editable_column?(c)
162
+ end
163
+ end
164
+
165
+ def set_default_sortable(c)
166
+ # this *has* to be set to false if we don't want the column to be sortable (it's sortable by default in Ext)
167
+ c[:sortable] = !(c[:virtual] && !c[:sorting_scope]) if c[:sortable].nil?
168
+ end
169
+
170
+ def set_default_filterable(c)
171
+ c[:filterable] = !c[:virtual] if c[:filterable].nil?
172
+ end
173
+
174
+
175
+ # Detects an association column and sets up the proper editor.
176
+ def set_default_association_editor(c)
177
+ assoc, assoc_method = c[:name].split('__')
178
+ return unless assoc
179
+
180
+ assoc_method_type = data_adapter.get_assoc_property_type assoc, assoc_method
181
+
182
+ # if association column is boolean, display a checkbox (or alike), otherwise - a combobox (or alike)
183
+ if c[:nested_attribute]
184
+ c[:editor] ||= editor_for_attr_type(assoc_method_type)
185
+ else
186
+ c[:editor] ||= assoc_method_type == :boolean ? editor_for_attr_type(:boolean) : editor_for_association
187
+ end
188
+ end
189
+
190
+ # If the column should be editable
191
+ def is_editable_column?(c)
192
+ not_editable_if = data_adapter.primary_key_attr?(c)
193
+ not_editable_if ||= c[:virtual] && !association_attr?(c[:name])
194
+ not_editable_if ||= c[:read_only]
195
+
196
+ editable_if = data_adapter.attribute_names.include?(c[:name])
197
+ editable_if ||= data_class.instance_methods.map(&:to_s).include?("#{c[:name]}=")
198
+ editable_if ||= association_attr?(c[:name])
199
+
200
+ editable_if && !not_editable_if
201
+ end
202
+
203
+ def initial_columns_order
204
+ final_columns.map do |c|
205
+ # copy the values that are not null
206
+ {name: c[:name]}.tap do |r|
207
+ r[:width] = c[:width] if c[:width]
208
+ r[:hidden] = c[:hidden] if c[:hidden]
209
+ end
210
+ end
211
+ end
212
+
213
+ def columns_order
214
+ if config[:persistence]
215
+ state[:columns_order] = initial_columns_order if columns_have_changed?
216
+ state[:columns_order] || initial_columns_order
217
+ else
218
+ initial_columns_order
219
+ end
220
+ end
221
+
222
+ def columns_have_changed?
223
+ init_column_names = initial_columns_order.map{ |c| c[:name].to_s }.sort
224
+ stored_column_names = (state[:columns_order] || initial_columns_order).map{ |c| c[:name].to_s }.sort
225
+ init_column_names != stored_column_names
226
+ end
227
+
228
+ # Column editor config for attribute type.
229
+ def editor_for_attr_type(type)
230
+ {:xtype => attr_type_to_editor_xtype_map[type] || :textfield}
231
+ end
232
+
233
+ # Column editor config for one-to-many association
234
+ def editor_for_association
235
+ {:xtype => :netzkeremotecombo}
236
+ end
237
+
238
+ # Hash that maps a column type to the editor xtype. Override if you want different editors.
239
+ def attr_type_to_editor_xtype_map
240
+ {
241
+ :integer => :numberfield,
242
+ :boolean => :checkbox,
243
+ :date => :datefield,
244
+ :datetime => :xdatetime,
245
+ :text => :textarea,
246
+ :string => :textfield
247
+ }
248
+ end
249
+
250
+ def attr_type_to_xtype_map
251
+ {
252
+ # :integer => :numbercolumn, # don't like the default formatter
253
+ :boolean => :checkcolumn,
254
+ :date => :datecolumn,
255
+ #:datetime => :datecolumn # TODO: replace with datetimepicker
256
+ }
257
+ end
258
+
259
+ # Default fields that will be displayed in the Add/Edit/Search forms
260
+ # When overriding this method, keep in mind that the fields inside the layout must be expanded (each field represented by a hash, not just a symbol)
261
+ def default_fields_for_forms
262
+ selected_columns = final_columns.select do |c|
263
+ data_adapter.attribute_names.include?(c[:name]) ||
264
+ data_class.instance_methods.include?("#{c[:name]}=") ||
265
+ association_attr?(c[:name])
266
+ end
267
+
268
+ selected_columns.map do |c|
269
+ field_config = {
270
+ :name => c[:name],
271
+ :field_label => c[:text] || c[:header]
272
+ }
273
+
274
+ # scopes for combobox options
275
+ field_config[:scopes] = c[:editor][:scopes] if c[:editor].is_a?(Hash)
276
+
277
+ field_config.merge!(c[:editor] || {})
278
+
279
+ field_config
280
+ end
281
+ end
282
+
283
+ def columns_default_values
284
+ final_columns.inject({}) do |r,c|
285
+ assoc_name, assoc_method = c[:name].split '__'
286
+ if c[:default_value].nil?
287
+ r
288
+ else
289
+ if assoc_method
290
+ r.merge(data_adapter.foreign_key_for(assoc_name) || data_adapter.foreign_key_for(assoc_name) => c[:default_value])
291
+ else
292
+ r.merge(c[:name] => c[:default_value])
293
+ end
294
+ end
295
+ end
296
+ end
297
+
298
+ # Recursively traversess items (an array) and yields each found field (a hash with :name set)
299
+ def each_attr_in(items)
300
+ items.each do |item|
301
+ if item.is_a?(Hash)
302
+ each_attr_in(item[:items]) if item[:items].is_a?(Array)
303
+ yield(item) if item[:name]
304
+ end
305
+ end
306
+ end
307
+ end
308
+ end
309
+ end
@@ -1,18 +1,14 @@
1
1
  module Netzke
2
2
  module Basepack
3
- # This module is included into such data-driven components as GridPanel, FormPanel, PagingFormPanel, etc.
3
+ # This module is included into such data-driven components as Grid, Form, PagingForm, etc.
4
4
  module DataAccessor
5
-
6
-
7
-
8
5
  # Returns options for comboboxes in grids/forms
9
6
  def combobox_options_for_column(column, method_options = {})
10
7
  data_adapter.combobox_options_for_column column, method_options
11
8
  end
12
9
 
13
10
  # Normalize array of attributes
14
- # [:col1, "col2", {:name => :col3}] =>
15
- # [{:name => "col1"}, {:name => "col2"}, {:name => "col3"}]
11
+ # [:col1, "col2", {:name => :col3}] #=> [{:name => "col1"}, {:name => "col2"}, {:name => "col3"}]
16
12
  def normalize_attrs(attrs)
17
13
  attrs.map{ |a| normalize_attr(a) }
18
14
  end
@@ -35,19 +31,33 @@ module Netzke
35
31
 
36
32
  # Data adapter responsible for all DB-related operations
37
33
  def data_adapter
38
- @data_adapter ||= Netzke::Basepack::DataAdapters::AbstractAdapter.adapter_class(data_class).new(data_class)
34
+ @data_adapter ||= data_class && Netzke::Basepack::DataAdapters::AbstractAdapter.adapter_class(data_class).new(data_class)
39
35
  end
40
36
 
41
- # whether a column is bound to the primary_key
42
- def primary_key_attr?(a)
43
- data_class && a[:name].to_s == data_class.primary_key.to_s
44
- end
37
+ # whether a column/field is bound to the primary_key
38
+ # def primary_key_attr?(a)
39
+ # data_class && a[:name].to_s == data_class.primary_key.to_s
40
+ # end
45
41
 
46
42
  # Mark an attribute as "virtual" by default, when it doesn't reflect a model column, or a model column of an association
47
43
  def set_default_virtual(c)
48
- c[:virtual] = data_adapter.column_virtual?(c) if c[:virtual].nil?
44
+ c[:virtual] = data_adapter.virtual_attribute?(c) if c[:virtual].nil?
49
45
  end
50
46
 
47
+ # Returns a hash of association attribute default values. Used when creating new records with association attributes that have a default value
48
+ def default_association_values(attr_hash) #:nodoc:
49
+ @_default_association_values ||= {}.tap do |values|
50
+ attr_hash.each_pair do |name,c|
51
+ next unless association_attr?(c) && c[:default_value]
52
+
53
+ assoc_name, assoc_method = c[:name].split '__'
54
+ assoc_class = data_adapter.class_for(assoc_name)
55
+ assoc_data_adapter = Netzke::Basepack::DataAdapters::AbstractAdapter.adapter_class(assoc_class).new(assoc_class)
56
+ assoc_instance = assoc_data_adapter.find_record c[:default_value]
57
+ values[name] = assoc_instance.send(assoc_method)
58
+ end
59
+ end
60
+ end
51
61
  end
52
62
  end
53
63
  end
@@ -2,6 +2,23 @@ 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
4
 
5
+ # Returns primary key name of the model
6
+ def primary_key_name
7
+ "id"
8
+ end
9
+
10
+ # Returns a list of model attribute hashes, each containing `name`, `attr_type` and `default_value` (if set in the schema).
11
+ # For association columns the name can have the double-underscore format, e.g.: `author__first_name`.
12
+ # These attributes will be used by grids and forms to display default columns/fields.
13
+ def model_attributes
14
+ raise NotImplementedError
15
+ end
16
+
17
+ # Returns attribute type (as Symbol) given its name.
18
+ def attr_type(attr_name)
19
+ raise NotImplementedError
20
+ end
21
+
5
22
  # Returns records based on passed params. Implements:
6
23
  # * pagination
7
24
  # * filtering
@@ -14,7 +31,7 @@ module Netzke::Basepack::DataAdapters
14
31
  # * :direction - "asc" or "desc"
15
32
  # * :limit - rows per page in pagination
16
33
  # * :start - page number in pagination
17
- # * :scope - the scope as described in Netzke::Basepack::GridPanel
34
+ # * :scope - the scope as described in Netzke::Basepack::Grid
18
35
  # * :filter - Ext filters
19
36
  #
20
37
  # The `columns` parameter may be used to use joins to address the n+1 query problem, and receives an array of column configurations
@@ -33,7 +50,7 @@ module Netzke::Basepack::DataAdapters
33
50
  #
34
51
  # `params` is a hash that contains the following keys:
35
52
  #
36
- # * :scope - the scope as described in Netzke::Basepack::GridPanel
53
+ # * :scope - the scope as described in Netzke::Basepack::Grid
37
54
  # * :filter - Ext filters
38
55
  #
39
56
  # The `columns` parameter may be used to use joins to address the n+1 query problem, and receives an array of column configurations
@@ -74,14 +91,18 @@ module Netzke::Basepack::DataAdapters
74
91
  def get_property_type column
75
92
  column.type
76
93
  end
77
-
94
+
78
95
  # should return true if column is virtual
79
- def column_virtual? c
96
+ def virtual_attribute? c
80
97
  raise NotImplementedError
81
98
  end
82
99
 
83
100
  # Returns options for comboboxes in grids/forms
84
- def combobox_options_for_column(column, method_options = {})
101
+ # +attr+ - column/field configuration; note that it will in its turn provide:
102
+ # * +name+ - attribute name
103
+ # * +scope+ - searching scope (optional)
104
+ # +query+ - whatever is entered in the combobox
105
+ def combo_data(attr, query = "")
85
106
  raise NotImplementedError
86
107
  end
87
108
 
@@ -99,6 +120,16 @@ module Netzke::Basepack::DataAdapters
99
120
  def destroy(ids)
100
121
  end
101
122
 
123
+ # Finds a record by id, return nil if not found
124
+ def find_record(id)
125
+ @model_class.find(id)
126
+ end
127
+
128
+ # Build a hash of foreign keys and the associated model
129
+ def hash_fk_model
130
+ raise NotImplementedError
131
+ end
132
+
102
133
  # Changes records position (e.g. when acts_as_list is used in ActiveRecord).
103
134
  #
104
135
  # `params` is a hash with the following keys:
@@ -125,22 +156,55 @@ module Netzke::Basepack::DataAdapters
125
156
  record.errors.to_a
126
157
  end
127
158
 
128
- # Finds a record by id, return nil if not found
129
- def find_record(id)
130
- @model_class.find(id)
159
+ # Whether an attribute is mass assignable. As second argument optionally takes the role.
160
+ def attribute_mass_assignable?(attr_name, role = :default)
161
+ true
131
162
  end
132
163
 
133
- # Build a hash of foreign keys and the associated model
134
- def hash_fk_model
135
- raise NotImplementedError
164
+ # Whether an attribute (by name) is an association one
165
+ def association_attr?(attr_name)
166
+ !!attr_name.to_s.index("__")
167
+ end
168
+
169
+ # Transforms a record to an array of values according to the passed attributes
170
+ # +attrs+ - array of attribute config hashes
171
+ def record_to_array(r, attrs)
172
+ []
136
173
  end
137
174
 
175
+ # Transforms a record to a hash of values according to the passed attributes
176
+ # +attrs+ - array of attribute config hashes
177
+ def record_to_hash(r, attrs)
178
+ {}
179
+ end
138
180
 
181
+ # Returns a hash of association values for given record, e.g.:
182
+ #
183
+ # {author__first_name: "Michael"}
184
+ def assoc_values(r, attr_hash) #:nodoc:
185
+ {}.tap do |values|
186
+ attr_hash.each_pair do |name,c|
187
+ values[name] = record_value_for_attribute(r, c, true) if association_attr?(c)
188
+ end
189
+ end
190
+ end
191
+
192
+ # Fetches the value specified by an (association) attribute
193
+ # If +through_association+ is true, get the value of the association by provided method, *not* the associated record's id
194
+ # E.g., author__name with through_association set to true may return "Vladimir Nabokov", while with through_association set to false, it'll return author_id for the current record
195
+ def record_value_for_attribute(r, a, through_association = false)
196
+ end
197
+
198
+ # Assigns new value to an (association) attribute in a given record
199
+ # +role+ - role provided for mass assignment protection
200
+ def set_record_value_for_attribute(record, attr, value, role = :default)
201
+ end
139
202
 
140
203
  # -- End of overridable methods
141
204
 
142
205
  # Abstract-adapter specifics
143
206
  #
207
+ #
144
208
 
145
209
  # Used to determine if the given adapter should be used for the passed in class.
146
210
  def self.for_class?(member_class)