netzke-basepack 0.7.7 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +15 -10
- data/{CHANGELOG.rdoc → CHANGELOG.md} +146 -110
- data/LICENSE +7 -1
- data/README.md +47 -56
- data/Rakefile +5 -5
- data/config/before-travis.sh +10 -0
- data/javascripts/basepack.js +0 -130
- data/javascripts/netzkeremotecombo.js +59 -0
- data/lib/netzke/basepack.rb +9 -14
- data/lib/netzke/basepack/accordion.rb +45 -0
- data/lib/netzke/basepack/active_record.rb +12 -0
- data/lib/netzke/basepack/active_record/relation_extensions.rb +27 -0
- data/lib/netzke/basepack/columns.rb +309 -0
- data/lib/netzke/basepack/data_accessor.rb +22 -12
- data/lib/netzke/basepack/data_adapters/abstract_adapter.rb +75 -11
- data/lib/netzke/basepack/data_adapters/active_record_adapter.rb +154 -49
- data/lib/netzke/basepack/fields.rb +162 -0
- data/lib/netzke/basepack/form.rb +136 -0
- data/lib/netzke/basepack/{form_panel → form}/javascripts/comma_list_cbg.js +0 -1
- data/lib/netzke/basepack/{form_panel/javascripts/form_panel.js → form/javascripts/form.js} +20 -26
- data/lib/netzke/basepack/{form_panel → form}/javascripts/n_radio_group.js +0 -1
- data/lib/netzke/basepack/{form_panel → form}/javascripts/readonly_mode.js +0 -0
- data/lib/netzke/basepack/form/services.rb +115 -0
- data/lib/netzke/basepack/{form_panel → form}/stylesheets/readonly_mode.css +0 -0
- data/lib/netzke/basepack/grid.rb +355 -0
- data/lib/netzke/basepack/{grid_panel → grid}/javascripts/advanced_search.js +1 -1
- data/lib/netzke/basepack/{grid_panel → grid}/javascripts/check_column_fix.js +0 -0
- data/lib/netzke/basepack/{grid_panel → grid}/javascripts/edit_in_form.js +3 -3
- data/lib/netzke/basepack/{grid_panel → grid}/javascripts/event_handling.js +5 -2
- data/lib/netzke/basepack/{grid_panel/javascripts/grid_panel.js → grid/javascripts/grid.js} +120 -132
- data/lib/netzke/basepack/{grid_panel → grid}/javascripts/misc.js +0 -0
- data/lib/netzke/basepack/grid/services.rb +216 -0
- data/lib/netzke/basepack/item_persistence.rb +44 -0
- data/lib/netzke/basepack/item_persistence/events_plugin.rb +47 -0
- data/lib/netzke/basepack/{paging_form_panel.rb → paging_form.rb} +24 -30
- data/lib/netzke/basepack/{paging_form_panel/javascripts/paging_form_panel.js → paging_form/javascripts/paging_form.js} +2 -4
- data/lib/netzke/basepack/query_builder.rb +44 -73
- data/lib/netzke/basepack/query_builder/javascripts/query_builder.js +16 -2
- data/lib/netzke/basepack/record_form_window.rb +67 -0
- data/lib/netzke/basepack/search_panel.rb +22 -24
- data/lib/netzke/basepack/search_panel/javascripts/condition_field.js +2 -2
- data/lib/netzke/basepack/search_window.rb +47 -53
- data/lib/netzke/basepack/simple_app.rb +10 -13
- data/lib/netzke/basepack/simple_app/javascripts/simple_app.js +2 -8
- data/lib/netzke/basepack/tab_panel.rb +5 -4
- data/lib/netzke/basepack/tab_panel/javascripts/tab_panel.js +5 -5
- data/lib/netzke/basepack/version.rb +2 -2
- data/lib/netzke/basepack/viewport.rb +16 -0
- data/lib/netzke/basepack/window.rb +27 -18
- data/lib/netzke/basepack/window/javascripts/window.js +7 -1
- data/lib/netzke/basepack/wrap_lazy_loaded.rb +18 -18
- data/locales/en.yml +40 -24
- data/netzke-basepack.gemspec +51 -82
- data/stylesheets/basepack.css +0 -41
- data/test/basepack_test_app/Gemfile +9 -46
- data/test/basepack_test_app/Gemfile.lock +61 -96
- data/test/basepack_test_app/app/components/author_form.rb +8 -5
- data/test/basepack_test_app/app/components/author_grid.rb +2 -2
- data/test/basepack_test_app/app/components/book_form.rb +34 -31
- data/test/basepack_test_app/app/components/book_form_with_defaults.rb +6 -7
- data/test/basepack_test_app/app/components/book_form_with_file_upload.rb +10 -0
- data/test/basepack_test_app/app/components/book_form_with_nested_attributes.rb +5 -6
- data/test/basepack_test_app/app/components/book_grid.rb +19 -8
- data/test/basepack_test_app/app/components/book_grid_filtering.rb +4 -7
- data/test/basepack_test_app/app/components/book_grid_loader.rb +28 -15
- data/test/basepack_test_app/app/components/book_grid_with_custom_columns.rb +45 -21
- data/test/basepack_test_app/app/components/book_grid_with_default_values.rb +26 -8
- data/test/basepack_test_app/app/components/book_grid_with_excluded_columns.rb +11 -0
- data/test/basepack_test_app/app/components/book_grid_with_extra_feedback.rb +2 -2
- data/test/basepack_test_app/app/components/book_grid_with_extra_filters.rb +7 -6
- data/test/basepack_test_app/app/components/book_grid_with_mass_assignment_security.rb +9 -0
- data/test/basepack_test_app/app/components/book_grid_with_nested_attributes.rb +9 -9
- data/test/basepack_test_app/app/components/book_grid_with_overridden_columns.rb +5 -3
- data/test/basepack_test_app/app/components/book_grid_with_paging.rb +6 -8
- data/test/basepack_test_app/app/components/book_grid_with_persistence.rb +6 -4
- data/test/basepack_test_app/app/components/book_grid_with_scope.rb +6 -0
- data/test/basepack_test_app/app/components/book_grid_with_scoped_authors.rb +10 -7
- data/test/basepack_test_app/app/components/book_grid_with_virtual_attributes.rb +21 -13
- data/test/basepack_test_app/app/components/book_paging_form.rb +21 -0
- data/test/basepack_test_app/app/components/book_query_builder.rb +7 -6
- data/test/basepack_test_app/app/components/book_with_custom_primary_key_grid.rb +6 -7
- data/test/basepack_test_app/app/components/books_bound_to_author.rb +9 -7
- data/test/basepack_test_app/app/components/border_layout_panel_with_persistence.rb +12 -0
- data/test/basepack_test_app/app/components/double_book_grid.rb +19 -14
- data/test/basepack_test_app/app/components/form_without_model.rb +15 -16
- data/test/basepack_test_app/app/components/grid_with_initial_sorting.rb +7 -0
- data/test/basepack_test_app/app/components/grid_with_inline_data.rb +7 -0
- data/test/basepack_test_app/app/components/paging_form_with_search.rb +2 -2
- data/test/basepack_test_app/app/components/panel_with_persistent_regions.rb +35 -0
- data/test/basepack_test_app/app/components/query_builder.rb +7 -0
- data/test/basepack_test_app/app/components/simple_panel.rb +16 -11
- data/test/basepack_test_app/app/components/simple_window.rb +7 -6
- data/test/basepack_test_app/app/components/some_accordion.rb +18 -0
- data/test/basepack_test_app/app/components/some_auth_app.rb +5 -5
- data/test/basepack_test_app/app/components/some_border_layout.rb +20 -20
- data/test/basepack_test_app/app/components/some_search_panel.rb +6 -0
- data/test/basepack_test_app/app/components/some_simple_app.rb +30 -16
- data/test/basepack_test_app/app/components/some_tab_panel.rb +18 -15
- data/test/basepack_test_app/app/components/user_form.rb +18 -16
- data/test/basepack_test_app/app/components/user_form_with_default_fields.rb +5 -6
- data/test/basepack_test_app/app/components/user_grid.rb +11 -6
- data/test/basepack_test_app/app/components/user_grid_with_customized_form_fields.rb +5 -3
- data/test/basepack_test_app/app/components/window_component_loader.rb +25 -21
- data/test/basepack_test_app/app/models/address.rb +0 -26
- data/test/basepack_test_app/app/models/author.rb +0 -31
- data/test/basepack_test_app/app/models/book.rb +1 -42
- data/test/basepack_test_app/app/models/book_with_custom_primary_key.rb +1 -23
- data/test/basepack_test_app/app/models/role.rb +0 -21
- data/test/basepack_test_app/app/models/user.rb +0 -24
- data/test/basepack_test_app/app/views/layouts/components.html.erb +1 -1
- data/test/basepack_test_app/config/application.rb +1 -1
- data/test/basepack_test_app/config/database.yml.travis +2 -6
- data/test/basepack_test_app/config/initializers/netzke.rb +1 -6
- data/test/basepack_test_app/db/schema.rb +14 -14
- data/test/basepack_test_app/features/accordion_panel.feature +2 -2
- data/test/basepack_test_app/features/form_panel.feature +7 -7
- data/test/basepack_test_app/features/grid_panel.feature +93 -39
- data/test/basepack_test_app/features/grid_panel_with_custom_primary_key.feature +2 -1
- data/test/basepack_test_app/features/grid_sorting.feature +30 -6
- data/test/basepack_test_app/features/paging_form_panel.feature +7 -7
- data/test/basepack_test_app/features/persistent_regions.feature +30 -0
- data/test/basepack_test_app/features/search_in_grid.feature +5 -5
- data/test/basepack_test_app/features/simple_app.feature +6 -7
- data/test/basepack_test_app/features/step_definitions/form_panel_steps.rb +1 -1
- data/test/basepack_test_app/features/step_definitions/generic_steps.rb +109 -4
- data/test/basepack_test_app/features/step_definitions/grid_panel_steps.rb +8 -10
- data/test/basepack_test_app/features/step_definitions/window_steps.rb +27 -0
- data/test/basepack_test_app/features/tab_panel.feature +1 -1
- data/test/basepack_test_app/features/window.feature +17 -0
- data/test/unit/accordion_panel_test.rb +2 -2
- data/test/unit/grid_panel_test.rb +4 -4
- metadata +57 -83
- data/TODO.rdoc +0 -8
- data/lib/generators/netzke/basepack_generator.rb +0 -10
- data/lib/generators/netzke/templates/assets/ts-checkbox.gif +0 -0
- data/lib/generators/netzke/templates/create_netzke_field_lists.rb +0 -18
- data/lib/netzke/active_record.rb +0 -20
- data/lib/netzke/active_record/attributes.rb +0 -259
- data/lib/netzke/active_record/combobox_options.rb +0 -16
- data/lib/netzke/active_record/relation_extensions.rb +0 -37
- data/lib/netzke/basepack/accordion_panel.rb +0 -39
- data/lib/netzke/basepack/action_column.rb +0 -68
- data/lib/netzke/basepack/action_column/javascripts/action_column.js +0 -61
- data/lib/netzke/basepack/auth_app.rb +0 -159
- data/lib/netzke/basepack/basic_app.rb +0 -7
- data/lib/netzke/basepack/border_layout_panel.rb +0 -53
- data/lib/netzke/basepack/border_layout_panel/javascripts/border_layout_panel.js +0 -40
- data/lib/netzke/basepack/data_adapters/data_mapper_adapter.rb +0 -264
- data/lib/netzke/basepack/data_adapters/sequel_adapter.rb +0 -260
- data/lib/netzke/basepack/form_panel.rb +0 -144
- data/lib/netzke/basepack/form_panel/fields.rb +0 -208
- data/lib/netzke/basepack/form_panel/javascripts/misc.js +0 -4
- data/lib/netzke/basepack/form_panel/services.rb +0 -142
- data/lib/netzke/basepack/grid_panel.rb +0 -441
- data/lib/netzke/basepack/grid_panel/columns.rb +0 -400
- data/lib/netzke/basepack/grid_panel/javascripts/rows-dd.js +0 -281
- data/lib/netzke/basepack/grid_panel/record_form_window.rb +0 -41
- data/lib/netzke/basepack/grid_panel/services.rb +0 -235
- data/lib/netzke/basepack/panel.rb +0 -11
- data/lib/netzke/basepack/wrapper.rb +0 -28
- data/lib/netzke/data_mapper.rb +0 -18
- data/lib/netzke/data_mapper/attributes.rb +0 -273
- data/lib/netzke/data_mapper/combobox_options.rb +0 -11
- data/lib/netzke/data_mapper/relation_extensions.rb +0 -38
- data/lib/netzke/sequel.rb +0 -18
- data/lib/netzke/sequel/attributes.rb +0 -274
- data/lib/netzke/sequel/combobox_options.rb +0 -10
- data/lib/netzke/sequel/relation_extensions.rb +0 -40
- data/locales/zh-cn.yml +0 -79
- data/test/basepack_test_app/app/components/book_form_with_custom_fields.rb +0 -21
- data/test/basepack_test_app/app/components/book_grid_with_column_actions.rb +0 -15
- data/test/basepack_test_app/app/components/book_grid_with_defaults.rb +0 -6
- data/test/basepack_test_app/app/components/book_paging_form_panel.rb +0 -22
- data/test/basepack_test_app/app/components/generic_user_form.rb +0 -12
- data/test/basepack_test_app/app/components/simple_accordion.rb +0 -11
- data/test/basepack_test_app/app/components/simple_tab_panel.rb +0 -11
- data/test/basepack_test_app/app/components/simple_wrapper.rb +0 -7
- data/test/basepack_test_app/app/components/some_accordion_panel.rb +0 -22
- data/test/basepack_test_app/app/presenters/forms/generic_user.rb +0 -6
- data/test/basepack_test_app/app/views/components/loadable_window.html.erb +0 -9
- data/test/basepack_test_app/app/views/components/simple_panel.html.erb +0 -1
- data/test/basepack_test_app/features/components_in_view.feature +0 -11
- data/test/basepack_test_app/features/simple_panel.feature +0 -11
- data/test/basepack_test_app/features/validations_in_grid.feature +0 -13
- data/test/basepack_test_app/features/virtual_attributes.feature +0 -16
- data/test/basepack_test_app/spec/components/form_panel_spec.rb +0 -53
- data/test/basepack_test_app/spec/components/grid_panel_spec.rb +0 -10
- data/test/basepack_test_app/spec/data_adapter/adapter_spec.rb +0 -68
- data/test/basepack_test_app/spec/data_adapter/attributes_spec.rb +0 -56
- data/test/basepack_test_app/spec/data_adapter/relation_extensions_spec.rb +0 -125
- data/test/basepack_test_app/spec/factories.rb +0 -28
- data/test/basepack_test_app/spec/spec_helper.rb +0 -39
@@ -1,4 +1,5 @@
|
|
1
1
|
module Netzke::Basepack::DataAdapters
|
2
|
+
# Implementation of Netzke::Basepack::DataAdapters::AbstractAdapter
|
2
3
|
class ActiveRecordAdapter < AbstractAdapter
|
3
4
|
def self.for_class?(model_class)
|
4
5
|
model_class <= ActiveRecord::Base
|
@@ -8,19 +9,15 @@ module Netzke::Basepack::DataAdapters
|
|
8
9
|
@model_class.primary_key.to_s
|
9
10
|
end
|
10
11
|
|
11
|
-
def attr_type(attr_name)
|
12
|
-
@model_class.association_attr?(attr_name) ? :integer : (@model_class.columns_hash[attr_name.to_s].try(:type) || :string)
|
13
|
-
end
|
14
|
-
|
15
12
|
def model_attributes
|
16
13
|
@model_class.column_names.map do |column_name|
|
17
|
-
{:
|
14
|
+
{name: column_name, attr_type: @model_class.columns_hash[column_name].type}.tap do |c|
|
18
15
|
|
19
16
|
# If it's named as foreign key of some association, then it's an association column
|
20
|
-
assoc = @model_class.reflect_on_all_associations.detect { |a|
|
17
|
+
assoc = @model_class.reflect_on_all_associations.detect { |a| a.foreign_key == c[:name] }
|
21
18
|
|
22
19
|
if assoc && !assoc.options[:polymorphic]
|
23
|
-
candidates = %w{name title label} <<
|
20
|
+
candidates = %w{name title label} << assoc.foreign_key
|
24
21
|
assoc_method = candidates.detect{|m| (assoc.klass.instance_methods.map(&:to_s) + assoc.klass.column_names).include?(m) }
|
25
22
|
c[:name] = "#{assoc.name}__#{assoc_method}"
|
26
23
|
end
|
@@ -33,6 +30,28 @@ module Netzke::Basepack::DataAdapters
|
|
33
30
|
end
|
34
31
|
end
|
35
32
|
|
33
|
+
# WIP
|
34
|
+
def attribute_names
|
35
|
+
@model_class.column_names
|
36
|
+
end
|
37
|
+
|
38
|
+
def primary_key_attr?(a)
|
39
|
+
a[:name].to_s == @model_class.primary_key.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def human_attribute_name(attr_name)
|
43
|
+
@model_class.human_attribute_name(attr_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def primary_key
|
47
|
+
@model_class.primary_key
|
48
|
+
end
|
49
|
+
## E_WIP
|
50
|
+
|
51
|
+
def attr_type(attr_name)
|
52
|
+
association_attr?(attr_name) ? :integer : (@model_class.columns_hash[attr_name.to_s].try(:type) || :string)
|
53
|
+
end
|
54
|
+
|
36
55
|
def get_records(params, columns=[])
|
37
56
|
# build initial relation based on passed params
|
38
57
|
relation = get_relation(params)
|
@@ -90,7 +109,7 @@ module Netzke::Basepack::DataAdapters
|
|
90
109
|
end
|
91
110
|
end
|
92
111
|
|
93
|
-
def
|
112
|
+
def virtual_attribute?(c)
|
94
113
|
assoc_name, asso = c[:name].split('__')
|
95
114
|
assoc, assoc_method = assoc_and_assoc_method_for_attr(c[:name])
|
96
115
|
|
@@ -101,50 +120,35 @@ module Netzke::Basepack::DataAdapters
|
|
101
120
|
end
|
102
121
|
end
|
103
122
|
|
104
|
-
|
105
|
-
|
106
|
-
query = method_options[:query]
|
123
|
+
def combo_data(attr, query = "")
|
124
|
+
assoc, assoc_method = assoc_and_assoc_method_for_attr(attr[:name])
|
107
125
|
|
108
|
-
|
109
|
-
|
110
|
-
if options
|
111
|
-
query ? options.select{ |o| o.index(/^#{query}/) }.map{ |el| [el] } : options
|
112
|
-
else
|
113
|
-
assoc, assoc_method = assoc_and_assoc_method_for_attr(column[:name])
|
114
|
-
|
115
|
-
if assoc
|
116
|
-
# Options for an asssociation attribute
|
117
|
-
|
118
|
-
relation = assoc.klass.scoped
|
126
|
+
if assoc
|
127
|
+
# Options for an asssociation attribute
|
119
128
|
|
120
|
-
|
121
|
-
|
122
|
-
if assoc.klass.column_names.include?(assoc_method)
|
123
|
-
# apply query
|
124
|
-
relation = relation.where(["#{assoc_method} like ?", "%#{query}%"]) if query.present?
|
125
|
-
relation.all.map{ |r| [r.id, r.send(assoc_method)] }
|
126
|
-
else
|
127
|
-
relation.all.map{ |r| [r.id, r.send(assoc_method)] }.select{ |id,value| value =~ /^#{query}/ }
|
128
|
-
end
|
129
|
+
relation = assoc.klass.scoped
|
130
|
+
relation = relation.extend_with(attr[:scope]) if attr[:scope]
|
129
131
|
|
132
|
+
if assoc.klass.column_names.include?(assoc_method)
|
133
|
+
# apply query
|
134
|
+
relation = relation.where(["#{assoc_method} like ?", "%#{query}%"]) if query.present?
|
135
|
+
relation.all.map{ |r| [r.id, r.send(assoc_method)] }
|
130
136
|
else
|
131
|
-
#
|
132
|
-
|
133
|
-
|
134
|
-
# ensure it is an array-in-array, as Ext will fail otherwise
|
135
|
-
raise RuntimeError, "netzke_combo_options_for should return an Array" unless res.kind_of? Array
|
136
|
-
return [[]] if res.empty?
|
137
|
-
|
138
|
-
unless res.first.kind_of? Array
|
139
|
-
res=res.map do |v|
|
140
|
-
[v]
|
141
|
-
end
|
142
|
-
end
|
143
|
-
return res
|
137
|
+
# an expensive search!
|
138
|
+
relation.all.map{ |r| [r.id, r.send(assoc_method)] }.select{ |id,value| value =~ /^#{query}/ }
|
144
139
|
end
|
140
|
+
|
141
|
+
else
|
142
|
+
distinct_combo_values(attr, query)
|
145
143
|
end
|
146
144
|
end
|
147
145
|
|
146
|
+
def distinct_combo_values(attr, query)
|
147
|
+
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}%'")
|
148
|
+
records.map{|r| [r.send(attr[:name]), r.send(attr[:name])]}
|
149
|
+
end
|
150
|
+
protected :distinct_combo_values
|
151
|
+
|
148
152
|
def foreign_key_for assoc_name
|
149
153
|
@model_class.reflect_on_association(assoc_name.to_sym).foreign_key
|
150
154
|
end
|
@@ -184,12 +188,110 @@ module Netzke::Basepack::DataAdapters
|
|
184
188
|
end
|
185
189
|
end
|
186
190
|
|
191
|
+
def record_to_array(r, attrs)
|
192
|
+
[].tap do |res|
|
193
|
+
attrs.each do |a|
|
194
|
+
res << record_value_for_attribute(r, a, a[:nested_attribute]) if a[:included] != false # :included ever used?..
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def record_to_hash(r, attrs)
|
200
|
+
{}.tap do |res|
|
201
|
+
attrs.each do |a|
|
202
|
+
res[a[:name].to_sym] = record_value_for_attribute(r, a, a[:nested_attribute]) if a[:included] != false
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# def assoc_values(r, attr_hash) #:nodoc:
|
208
|
+
# @_assoc_values ||= {}.tap do |values|
|
209
|
+
# attr_hash.each_pair do |name,c|
|
210
|
+
# values[name] = record_value_for_attribute(r, c, true) if association_attr?(c)
|
211
|
+
# end
|
212
|
+
# end
|
213
|
+
# end
|
214
|
+
|
215
|
+
def record_value_for_attribute(r, a, through_association = false)
|
216
|
+
v = if a[:getter]
|
217
|
+
a[:getter].call(r)
|
218
|
+
elsif r.respond_to?("#{a[:name]}")
|
219
|
+
r.send("#{a[:name]}")
|
220
|
+
elsif association_attr?(a)
|
221
|
+
split = a[:name].to_s.split(/\.|__/)
|
222
|
+
assoc = @model_class.reflect_on_association(split.first.to_sym)
|
223
|
+
if through_association
|
224
|
+
split.inject(r) do |r,m| # TODO: do we really need to descend deeper than 1 level?
|
225
|
+
if r.respond_to?(m)
|
226
|
+
r.send(m)
|
227
|
+
else
|
228
|
+
logger.debug "Netzke::Basepack: Wrong attribute name: #{a[:name]}" unless r.nil?
|
229
|
+
nil
|
230
|
+
end
|
231
|
+
end
|
232
|
+
else
|
233
|
+
r.send("#{assoc.options[:foreign_key] || assoc.name.to_s.foreign_key}")
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# a work-around for to_json not taking the current timezone into account when serializing ActiveSupport::TimeWithZone
|
238
|
+
v = v.to_datetime.to_s(:db) if [ActiveSupport::TimeWithZone].include?(v.class)
|
239
|
+
|
240
|
+
v
|
241
|
+
end
|
242
|
+
|
243
|
+
def set_record_value_for_attribute(r, a, v, role = :default)
|
244
|
+
v = v.to_time_in_current_zone if v.is_a?(Date) # convert Date to Time
|
245
|
+
|
246
|
+
if a[:setter]
|
247
|
+
a[:setter].call(r, v)
|
248
|
+
elsif r.respond_to?("#{a[:name]}=") && attribute_mass_assignable?(a[:name], role)
|
249
|
+
r.send("#{a[:name]}=", v)
|
250
|
+
elsif association_attr?(a)
|
251
|
+
split = a[:name].to_s.split(/\.|__/)
|
252
|
+
if a[:nested_attribute]
|
253
|
+
# We want:
|
254
|
+
# set_value_for_attribute({:name => :assoc_1__assoc_2__method, :nested_attribute => true}, 100)
|
255
|
+
# =>
|
256
|
+
# r.assoc_1.assoc_2.method = 100
|
257
|
+
split.inject(r) { |r,m| m == split.last ? (r && r.send("#{m}=", v) && r.save) : r.send(m) }
|
258
|
+
else
|
259
|
+
if split.size == 2
|
260
|
+
# search for association and assign it to r
|
261
|
+
assoc = @model_class.reflect_on_association(split.first.to_sym)
|
262
|
+
assoc_method = split.last
|
263
|
+
if assoc
|
264
|
+
if assoc.macro == :has_one
|
265
|
+
assoc_instance = r.send(assoc.name)
|
266
|
+
if assoc_instance
|
267
|
+
assoc_instance.send("#{assoc_method}=", v)
|
268
|
+
assoc_instance.save # what should we do when this fails?..
|
269
|
+
else
|
270
|
+
# what should we do in this case?
|
271
|
+
end
|
272
|
+
else
|
273
|
+
|
274
|
+
# set the foreign key to the passed value
|
275
|
+
# not that if a negative value is passed, we reset the association (set it to nil)
|
276
|
+
r.send("#{assoc.foreign_key}=", v.to_i < 0 ? nil : v) if attribute_mass_assignable?(assoc.foreign_key, role)
|
277
|
+
end
|
278
|
+
else
|
279
|
+
logger.debug "Netzke::Basepack: Association #{assoc} is not known for class #{@data_class}"
|
280
|
+
end
|
281
|
+
else
|
282
|
+
logger.debug "Netzke::Basepack: Wrong attribute name: #{a[:name]}"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
187
288
|
# Returns association and association method for a column
|
188
289
|
def assoc_and_assoc_method_for_attr(column_name)
|
189
290
|
assoc_name, assoc_method = column_name.split('__')
|
190
291
|
assoc = @model_class.reflect_on_association(assoc_name.to_sym) if assoc_method
|
191
292
|
[assoc, assoc_method]
|
192
293
|
end
|
294
|
+
protected :assoc_and_assoc_method_for_attr
|
193
295
|
|
194
296
|
|
195
297
|
# An ActiveRecord::Relation instance encapsulating all the necessary conditions.
|
@@ -200,11 +302,6 @@ module Netzke::Basepack::DataAdapters
|
|
200
302
|
|
201
303
|
relation = apply_column_filters(relation, params[:filter]) if params[:filter]
|
202
304
|
|
203
|
-
if params[:extra_conditions]
|
204
|
-
extra_conditions = normalize_extra_conditions(ActiveSupport::JSON.decode(params[:extra_conditions]))
|
205
|
-
relation = relation.extend_with_netzke_conditions(extra_conditions) if params[:extra_conditions]
|
206
|
-
end
|
207
|
-
|
208
305
|
query = params[:query] && ActiveSupport::JSON.decode(params[:query])
|
209
306
|
|
210
307
|
if query.present?
|
@@ -223,6 +320,7 @@ module Netzke::Basepack::DataAdapters
|
|
223
320
|
|
224
321
|
relation
|
225
322
|
end
|
323
|
+
protected :get_relation
|
226
324
|
|
227
325
|
# Parses and applies grid column filters, calling consequent "where" methods on the passed relation.
|
228
326
|
# Returns the updated relation.
|
@@ -282,6 +380,7 @@ module Netzke::Basepack::DataAdapters
|
|
282
380
|
|
283
381
|
res
|
284
382
|
end
|
383
|
+
protected :apply_column_filters
|
285
384
|
|
286
385
|
def predicates_for_and_conditions(conditions)
|
287
386
|
return nil if conditions.empty?
|
@@ -303,6 +402,12 @@ module Netzke::Basepack::DataAdapters
|
|
303
402
|
# join them by AND
|
304
403
|
predicates[1..-1].inject(predicates.first){ |r,p| r.and(p) }
|
305
404
|
end
|
405
|
+
protected :predicates_for_and_conditions
|
306
406
|
|
407
|
+
# Whether an attribute is mass assignable. As second argument optionally takes the role.
|
408
|
+
def attribute_mass_assignable?(attr_name, role = :default)
|
409
|
+
@model_class.accessible_attributes(role).empty? ? !@model_class.protected_attributes(role).include?(attr_name.to_s) : @model_class.accessible_attributes(role).include?(attr_name.to_s)
|
410
|
+
end
|
411
|
+
protected :attribute_mass_assignable?
|
307
412
|
end
|
308
413
|
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module Netzke
|
2
|
+
module Basepack
|
3
|
+
# Because Form allows for arbitrary layout of fields, we need to have all fields configured in one place (the +fields+ method), and then have references to those fields from +items+.
|
4
|
+
module Fields
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# Items with normalized fields (i.e. containing all the necessary attributes needed by Ext.form.Form to render a field)
|
8
|
+
def items
|
9
|
+
config.items || data_class && data_adapter.model_attributes || []
|
10
|
+
end
|
11
|
+
|
12
|
+
# Hash of fully configured fields, that are referenced in the items. E.g.:
|
13
|
+
# {
|
14
|
+
# :role__name => {:xtype => 'netzkeremotecombo', :disabled => true, :value => "admin"},
|
15
|
+
# :created_at => {:xtype => 'datetime', :disabled => true, :value => "2010-10-10 10:10"}
|
16
|
+
# }
|
17
|
+
def fields
|
18
|
+
@fields ||= begin
|
19
|
+
# extract incomplete field configs from +config+
|
20
|
+
flds = fields_from_config
|
21
|
+
|
22
|
+
primary_key = data_adapter.primary_key_name
|
23
|
+
flds[primary_key.to_sym] ||= {name: primary_key}
|
24
|
+
|
25
|
+
# and merged them with fields from the model
|
26
|
+
deep_merge_existing_fields(flds, fields_from_model) if data_adapter
|
27
|
+
flds
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# The array of fields as specified on the model level (using +netzke_attribute+ and alike)
|
32
|
+
def fields_array_from_model
|
33
|
+
data_adapter && data_adapter.model_attributes
|
34
|
+
end
|
35
|
+
|
36
|
+
# Hash of fields as specified on the model level
|
37
|
+
def fields_from_model
|
38
|
+
@fields_from_model ||= fields_array_from_model && fields_array_from_model.inject({}){ |hsh, f| hsh.merge(f[:name].to_sym => f) }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Hash of normalized field configs extracted from :items, e.g.:
|
42
|
+
#
|
43
|
+
# {:role__name => {:xtype => "netzkeremotecombo"}, :password => {:xtype => "passwordfield"}}
|
44
|
+
def fields_from_config
|
45
|
+
@fields_from_config || (normalize_config || true) && @fields_from_config
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
# This is where we expand our basic field config with all the defaults
|
51
|
+
def extend_item(field)
|
52
|
+
field = super
|
53
|
+
|
54
|
+
if is_field_config?(field)
|
55
|
+
# field can only be a string, a symbol, or a hash
|
56
|
+
if field.is_a?(Hash)
|
57
|
+
field = field.dup # we don't want to modify original hash
|
58
|
+
return field if field[:no_binding] # stop here if no normalization is needed
|
59
|
+
field[:name] = field[:name].to_s if field[:name] # all names should be strings
|
60
|
+
else
|
61
|
+
field = {:name => field.to_s}
|
62
|
+
end
|
63
|
+
|
64
|
+
# right place?
|
65
|
+
@fields_from_config[field[:name].to_sym] = field
|
66
|
+
|
67
|
+
field_from_model = fields_from_model && fields_from_model[field[:name].to_sym]
|
68
|
+
|
69
|
+
field_from_model && field.merge!(field_from_model)
|
70
|
+
|
71
|
+
detect_association_with_method(field) # xtype for an association field
|
72
|
+
set_default_field_label(field)
|
73
|
+
set_default_field_xtype(field) if field[:xtype].nil?
|
74
|
+
set_default_read_only(field)
|
75
|
+
|
76
|
+
# provide our special combobox with our id
|
77
|
+
field[:parent_id] = self.js_id if field[:xtype] == :netzkeremotecombo
|
78
|
+
|
79
|
+
field[:hidden] = field[:hide_label] = true if field[:hidden].nil? && data_adapter.try(:primary_key_attr?, field)
|
80
|
+
|
81
|
+
# checkbox setup
|
82
|
+
if field[:attr_type] == :boolean
|
83
|
+
field[:checked] = field[:value]
|
84
|
+
field[:unchecked_value] = false
|
85
|
+
field[:input_value] = true if field[:attr_type] == :boolean
|
86
|
+
end
|
87
|
+
|
88
|
+
# date field format
|
89
|
+
if field[:attr_type] = :date
|
90
|
+
field[:submit_format] = "Y-m-d"
|
91
|
+
field[:format] ||= "Y-m-d"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
field
|
96
|
+
end
|
97
|
+
|
98
|
+
# Sets the proper xtype of an asociation field
|
99
|
+
def detect_association_with_method(c)
|
100
|
+
if c[:name].index('__')
|
101
|
+
assoc_name, method = c[:name].split('__').map(&:to_sym)
|
102
|
+
assoc_method_type = data_adapter.get_assoc_property_type(assoc_name, method)
|
103
|
+
if c[:nested_attribute]
|
104
|
+
c[:xtype] ||= xtype_for_attr_type(assoc_method_type)
|
105
|
+
else
|
106
|
+
c[:xtype] ||= assoc_method_type == :boolean ? xtype_for_attr_type(assoc_method_type) : xtype_for_association
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def is_field_config?(item)
|
112
|
+
item.is_a?(Symbol) || (item.is_a?(Hash) && item[:name]) # && !is_component_config?(item)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Deeply merges only those key/values at the top level that are already there
|
116
|
+
def deep_merge_existing_fields(dest, src)
|
117
|
+
dest.each_pair do |k,v|
|
118
|
+
v.deep_merge!(src[k] || {})
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def set_default_field_label(c)
|
123
|
+
# multiple spaces (in case of association attrs) get replaced with one
|
124
|
+
c[:field_label] ||= data_class ? data_class.human_attribute_name(c[:name]) : c[:name].humanize
|
125
|
+
c[:field_label].gsub!(/\s+/, " ")
|
126
|
+
end
|
127
|
+
|
128
|
+
def set_default_field_xtype(field)
|
129
|
+
field[:xtype] = xtype_for_attr_type(field[:attr_type]) unless xtype_for_attr_type(field[:attr_type]).nil?
|
130
|
+
end
|
131
|
+
|
132
|
+
def set_default_read_only(field)
|
133
|
+
enabled_if = !data_adapter || data_adapter.attribute_names.include?(field[:name])
|
134
|
+
enabled_if ||= data_class.instance_methods.map(&:to_s).include?("#{field[:name]}=")
|
135
|
+
enabled_if ||= record && record.respond_to?("#{field[:name]}=")
|
136
|
+
enabled_if ||= association_attr?(field[:name])
|
137
|
+
|
138
|
+
field[:read_only] = !enabled_if if field[:read_only].nil?
|
139
|
+
end
|
140
|
+
|
141
|
+
def attr_type_to_xtype_map
|
142
|
+
{
|
143
|
+
:integer => :numberfield,
|
144
|
+
:boolean => config[:multi_edit] ? :tricheckbox : :checkboxfield,
|
145
|
+
:date => :datefield,
|
146
|
+
:datetime => :xdatetime,
|
147
|
+
:text => :textarea,
|
148
|
+
:json => :jsonfield,
|
149
|
+
:string => :textfield
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
def xtype_for_attr_type(type)
|
154
|
+
attr_type_to_xtype_map[type] || :textfield
|
155
|
+
end
|
156
|
+
|
157
|
+
def xtype_for_association
|
158
|
+
:netzkeremotecombo
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require "netzke/basepack/form/services"
|
2
|
+
|
3
|
+
module Netzke
|
4
|
+
module Basepack
|
5
|
+
# Ext.form.Panel-based component
|
6
|
+
#
|
7
|
+
# == Netzke-specific config options
|
8
|
+
#
|
9
|
+
# * +model+ - name of the ActiveRecord model that provides data to this Grid.
|
10
|
+
# * +record+ - record to be displayd in the form. Takes precedence over +:record_id+
|
11
|
+
# * +record_id+ - id of the record to be displayd in the form. Also see +:record+
|
12
|
+
# * +items+ - the layout of the fields as an array. See "Layout configuration".
|
13
|
+
# * +mode+ - render mode, accepted options:
|
14
|
+
# * +lockable+ - makes the form panel load initially in "display mode", then lets "unlock" it, change the values, and "lock" it again, while updating the values on the server
|
15
|
+
# * +updateMask+ - +Ext.LoadMask+ config options for the mask shown while the form is submitting its values
|
16
|
+
#
|
17
|
+
# === Layout configuration
|
18
|
+
#
|
19
|
+
# The layout of the form is configured by supplying the +item+ config option, same way it would be configured in Ext (thus allowing for complex form layouts). Form will expand fields by looking at their names (unless +no_binding+ set to +true+ is specified for a specific field).
|
20
|
+
#
|
21
|
+
# == Endpoints
|
22
|
+
# Form implements the following endpoints:
|
23
|
+
#
|
24
|
+
# * +netzke_load+ - loads a record with a given id from the server, e.g.:
|
25
|
+
#
|
26
|
+
# someForm.netzkeLoad({id: 100});
|
27
|
+
#
|
28
|
+
# * +netzke_submit+ - gets called when the form gets submitted (e.g. by pressing the Apply button, or by calling onApply)
|
29
|
+
# * +get_combobox_options+ - gets called when a 'remote' combobox field gets expanded
|
30
|
+
class Form < Netzke::Base
|
31
|
+
include self::Services
|
32
|
+
include Fields
|
33
|
+
include DataAccessor
|
34
|
+
include Netzke::Core::ConfigToDslDelegator
|
35
|
+
|
36
|
+
js_configure do |c|
|
37
|
+
c.extend = "Ext.form.Panel"
|
38
|
+
c.mixin
|
39
|
+
c.require :comma_list_cbg, :n_radio_group, :readonly_mode
|
40
|
+
end
|
41
|
+
|
42
|
+
delegates_to_dsl :model, :record_id
|
43
|
+
|
44
|
+
def js_configure(c)
|
45
|
+
super
|
46
|
+
|
47
|
+
configure_locked(c)
|
48
|
+
configure_bbar(c)
|
49
|
+
|
50
|
+
# prepend the primary key field if not present
|
51
|
+
if data_adapter
|
52
|
+
c.items = [extend_item(data_adapter.primary_key_name.to_sym), *c.items] if !includes_primary_key_field?(c.items)
|
53
|
+
c.pri = data_adapter.primary_key_name
|
54
|
+
end
|
55
|
+
|
56
|
+
if !c.multi_edit
|
57
|
+
c.record = js_record_data if record
|
58
|
+
else
|
59
|
+
c.record_id = c.record = nil if c.multi_edit # never set record_id in multi-edit mode
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
action :apply do |a|
|
64
|
+
a.icon = :tick
|
65
|
+
end
|
66
|
+
|
67
|
+
action :edit do |a|
|
68
|
+
a.icon = :pencil
|
69
|
+
end
|
70
|
+
|
71
|
+
action :cancel do |a|
|
72
|
+
a.icon = :cancel
|
73
|
+
end
|
74
|
+
|
75
|
+
def configure_locked(c)
|
76
|
+
c[:locked] = c[:locked].nil? ? (c[:mode] == :lockable) : c[:locked]
|
77
|
+
end
|
78
|
+
|
79
|
+
def configure_bbar(c)
|
80
|
+
c[:bbar] = [:apply] if c[:bbar].nil? && !c[:read_only]
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# Extra JavaScripts and stylesheets
|
85
|
+
css_configure do |c|
|
86
|
+
c.require :readonly_mode
|
87
|
+
end
|
88
|
+
|
89
|
+
# A hash of record data including the meta field
|
90
|
+
def js_record_data
|
91
|
+
data_adapter.record_to_hash(record, fields.values).merge(:meta => meta_field).literalize_keys
|
92
|
+
end
|
93
|
+
|
94
|
+
def record
|
95
|
+
@record ||= config[:record] || config[:record_id] && data_adapter && data_adapter.find_record(config[:record_id])
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def includes_primary_key_field?(items)
|
101
|
+
!!items.detect do |item|
|
102
|
+
(item.is_a?(Hash) ? item[:name] : item.to_s) == data_adapter.primary_key_name
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def normalize_config
|
107
|
+
config.items = items
|
108
|
+
@fields_from_config = {}
|
109
|
+
super
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.server_side_config_options
|
113
|
+
super + [:scope]
|
114
|
+
end
|
115
|
+
|
116
|
+
def meta_field
|
117
|
+
{}.tap do |res|
|
118
|
+
assoc_values = get_association_values
|
119
|
+
res[:association_values] = assoc_values.literalize_keys if record && !assoc_values.empty?
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def get_association_values
|
124
|
+
fields_that_need_associated_values = fields.select{ |k,v| k.to_s.index("__") && !fields[k][:nested_attribute] }
|
125
|
+
# Take care of Ruby 1.8.7
|
126
|
+
if fields_that_need_associated_values.is_a?(Array)
|
127
|
+
fields_that_need_associated_values = fields_that_need_associated_values.inject({}){|r,(k,v)| r.merge(k => v)}
|
128
|
+
end
|
129
|
+
|
130
|
+
fields_that_need_associated_values.each_pair.inject({}) do |r,(k,v)|
|
131
|
+
r.merge(k => data_adapter.record_value_for_attribute(record, fields_that_need_associated_values[k], true))
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|