netzke-core 0.4.4 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +14 -0
- data/README.rdoc +7 -2
- data/Rakefile +7 -5
- data/javascripts/core.js +88 -48
- data/lib/netzke/base.rb +233 -209
- data/lib/netzke/base_js.rb +56 -18
- data/lib/netzke/controller_extensions.rb +2 -6
- data/lib/netzke/core_ext.rb +18 -0
- data/test/unit/core_ext_test.rb +26 -22
- data/test/unit/netzke_core_test.rb +24 -25
- metadata +3 -4
- data/VERSION +0 -1
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
= edge
|
2
|
+
* API change: Netzke::Base: <tt>id_name</tt> accessor renamed to <tt>global_id</tt>
|
3
|
+
* Code: several internal code changes
|
4
|
+
* Code: lightly better test coverage
|
5
|
+
* New: <tt>Netzke::Base#global_id_by_reference</tt> method
|
6
|
+
* Compatibility: resolving conflicts with the <tt>api</tt> property in some Ext v3.0 components
|
7
|
+
* Fix: <tt>load_aggregatee_with_cache</tt> was throwing exception when the requested aggregatee wasn't defined
|
8
|
+
* New: <tt>persistent_config_id</tt> configuration option allows specifying an id by which persistent configuration is identified for the widget. Handy if different homogeneous widgets need to share the same persistent configuration.
|
9
|
+
* New: <tt>Netzke::Base#persistent_config</tt> method now accepts an optional boolean parameter signalizing that the configuration is global (not bound to a widget)
|
10
|
+
* Impr: cleaner handling of actions and toolbars; fbar configuration introduced.
|
11
|
+
* Impr: calling an API method now provides for the result value (if return by the server) in the callback.
|
12
|
+
* Impr: allows name spaced creation of Netzke widgets, e.g. widgets can now be defined under any module under Netzke, not only *directly* under Netzke.
|
13
|
+
* New: support for Ext.Window-based widgets (it'll call show() on them when the "*_widget_render" helper is used).
|
14
|
+
|
1
15
|
= v0.4.4 - 2009-10-12
|
2
16
|
* API change: default handlers for actions and tools are now supposed to be prefixed with "on". E.g.: if you declare an action named <tt>clear_table</tt>, the handler must be called (in Ruby) <tt>on_clear_table</tt> (mapped to <tt>onClearTable</tt> in JavaScript).
|
3
17
|
* Internal: the JavaScript instance now knows if persistent config is enabled (by checking this.persistentConfig).
|
data/README.rdoc
CHANGED
@@ -6,10 +6,15 @@ This is the bare bones of the Netzke framework. Use it to build your own widgets
|
|
6
6
|
The idea behind the Netzke framework is that it allows you write reusable client/server code. Create a widget, and then embed it (or load it dynamically) into your Ext-based applications or HTML pages. For more info, see the links below.
|
7
7
|
|
8
8
|
== Instalation
|
9
|
-
|
9
|
+
The gem is hosted on gemcutter. If you haven't yet enabled gemcutter, run the following:
|
10
|
+
|
11
|
+
sudo gem install gemcutter && gem tumble
|
12
|
+
|
13
|
+
Install the gem
|
14
|
+
|
10
15
|
sudo gem install netzke-core
|
11
16
|
|
12
|
-
If you want the most recent changes, install this as a plugin:
|
17
|
+
If you want to experiment with the most recent changes, install this as a plugin:
|
13
18
|
|
14
19
|
script/plugin install git://github.com/skozlov/netzke-core.git
|
15
20
|
|
data/Rakefile
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
begin
|
2
2
|
require 'jeweler'
|
3
3
|
Jeweler::Tasks.new do |gemspec|
|
4
|
+
gemspec.version = "0.4.5"
|
4
5
|
gemspec.name = "netzke-core"
|
5
6
|
gemspec.summary = "Build ExtJS/Rails widgets with minimum effort"
|
6
|
-
gemspec.description = "
|
7
|
+
gemspec.description = "Allows building ExtJS/Rails reusable code in a DRY way"
|
7
8
|
gemspec.email = "sergei@playcode.nl"
|
8
9
|
gemspec.homepage = "http://github.com/skozlov/netzke-core"
|
9
10
|
gemspec.rubyforge_project = "netzke-core"
|
10
11
|
gemspec.authors = ["Sergei Kozlov"]
|
11
12
|
end
|
12
|
-
Jeweler::
|
13
|
-
|
14
|
-
|
13
|
+
Jeweler::GemcutterTasks.new
|
14
|
+
# Jeweler::RubyforgeTasks.new do |rubyforge|
|
15
|
+
# rubyforge.doc_task = "rdoc"
|
16
|
+
# end
|
15
17
|
|
16
18
|
rescue LoadError
|
17
|
-
puts "Jeweler not available. Install it with: sudo gem install
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
18
20
|
end
|
19
21
|
|
20
22
|
require 'rake/rdoctask'
|
data/javascripts/core.js
CHANGED
@@ -4,8 +4,7 @@ This file gets loaded along with the rest of Ext library at the initial load
|
|
4
4
|
|
5
5
|
Ext.BLANK_IMAGE_URL = "/extjs/resources/images/default/s.gif";
|
6
6
|
Ext.namespace('Ext.netzke'); // namespace for extensions that depend on Ext
|
7
|
-
Ext.namespace('Netzke'); // namespace for extensions that do not depend on Ext
|
8
|
-
Ext.netzke.cache = {};
|
7
|
+
Ext.namespace('Netzke.classes'); // namespace for extensions that do not depend on Ext
|
9
8
|
|
10
9
|
Ext.QuickTips.init(); // seems obligatory in Ext v2.2.1, otherwise Ext.Component#destroy() stops working properly
|
11
10
|
|
@@ -88,18 +87,19 @@ Ext.widgetMixIn = {
|
|
88
87
|
height: 400,
|
89
88
|
// width: 800,
|
90
89
|
border: false,
|
91
|
-
|
90
|
+
isNetzke: true, // to distinguish Netzke components from regular Ext components
|
91
|
+
latestResult: {}, // latest result returned from the server via an API call
|
92
92
|
|
93
93
|
/*
|
94
94
|
Loads aggregatee into a container.
|
95
95
|
*/
|
96
96
|
loadAggregatee: function(params){
|
97
|
-
// params that will be provided for the server API call (load_aggregatee_with_cache); all what's passed in params.params is merged in
|
97
|
+
// params that will be provided for the server API call (load_aggregatee_with_cache); all what's passed in params.params is merged in. This way we exclude from sending along such things as :scope, :callback, etc.
|
98
98
|
var apiParams = Ext.apply({id: params.id, container: params.container}, params.params);
|
99
99
|
|
100
100
|
// build the cached widgets list to send it to the server
|
101
101
|
var cachedWidgetNames = [];
|
102
|
-
for (name in
|
102
|
+
for (name in Netzke.classes) {
|
103
103
|
cachedWidgetNames.push(name);
|
104
104
|
}
|
105
105
|
apiParams.cache = Ext.encode(cachedWidgetNames);
|
@@ -112,7 +112,7 @@ Ext.widgetMixIn = {
|
|
112
112
|
// visually disable the container while the widget is being loaded
|
113
113
|
// Ext.getCmp(params.container).disable();
|
114
114
|
|
115
|
-
Ext.getCmp(params.container).removeChild(); // remove the old widget
|
115
|
+
if (params.container) Ext.getCmp(params.container).removeChild(); // remove the old widget if the container is specified
|
116
116
|
|
117
117
|
// do the remote API call
|
118
118
|
this.loadAggregateeWithCache(apiParams);
|
@@ -177,7 +177,11 @@ Ext.widgetMixIn = {
|
|
177
177
|
*/
|
178
178
|
renderWidgetInContainer : function(params){
|
179
179
|
var cont = Ext.getCmp(params.container);
|
180
|
-
cont
|
180
|
+
if (cont) {
|
181
|
+
cont.instantiateChild(params.config);
|
182
|
+
} else {
|
183
|
+
this.instantiateChild(params.config);
|
184
|
+
}
|
181
185
|
},
|
182
186
|
|
183
187
|
/*
|
@@ -261,13 +265,18 @@ Ext.widgetMixIn = {
|
|
261
265
|
}
|
262
266
|
},
|
263
267
|
|
264
|
-
// Common handler for actions
|
265
|
-
actionHandler : function(
|
268
|
+
// Common handler for all widget's actions. <tt>comp</tt> is the Component that triggered the action (e.g. button or menu item)
|
269
|
+
actionHandler : function(comp){
|
270
|
+
var actionName = comp.name;
|
266
271
|
// If firing corresponding event doesn't return false, call the handler
|
267
|
-
if (this.fireEvent(
|
268
|
-
var
|
269
|
-
|
270
|
-
|
272
|
+
if (this.fireEvent(actionName+'click', comp)) {
|
273
|
+
var action = this.actions[actionName];
|
274
|
+
var customHandler = action.initialConfig.customHandler;
|
275
|
+
var methodName = (customHandler && customHandler.camelize(true)) || "on" + actionName.camelize();
|
276
|
+
if (!this[methodName]) {throw "Netzke: action handler '" + methodName + "' is undefined"}
|
277
|
+
|
278
|
+
// call the handler passing it the triggering component
|
279
|
+
this[methodName](comp);
|
271
280
|
}
|
272
281
|
},
|
273
282
|
|
@@ -292,10 +301,10 @@ Ext.widgetMixIn = {
|
|
292
301
|
// execute commands from server
|
293
302
|
this.bulkExecute(Ext.decode(response.responseText));
|
294
303
|
|
295
|
-
//
|
304
|
+
// provide callback if needed
|
296
305
|
if (typeof callback == 'function') {
|
297
306
|
if (!scope) scope = this;
|
298
|
-
callback.apply(scope);
|
307
|
+
callback.apply(scope, [this.latestResult]);
|
299
308
|
}
|
300
309
|
}
|
301
310
|
},
|
@@ -303,9 +312,15 @@ Ext.widgetMixIn = {
|
|
303
312
|
});
|
304
313
|
},
|
305
314
|
|
306
|
-
|
307
|
-
|
308
|
-
|
315
|
+
setResult: function(result) {
|
316
|
+
this.latestResult = result;
|
317
|
+
},
|
318
|
+
|
319
|
+
/* Normalize an array of abstracted button configs into an array of Ext button configs according to the following rules:
|
320
|
+
- if the element is a string and <tt>scope</tt> has an action with this name, replace this element with that action; if <tt>scope</tt> has no corresponding action, don't do anything (Ext will take care of it - display as text, or a separator, etc)
|
321
|
+
- if the element is an object, then:
|
322
|
+
-- if this object has a <tt>menu</tt> property - it's a nested menu; the value is expected to be an array of abstracted button configs, so, proceed recursively.
|
323
|
+
-- if this object has a <tt>handler</tt> property and the value correspond to a function in <tt>scope</tt> - replace this value with the reference to that function
|
309
324
|
*/
|
310
325
|
normalizeMenuItems: function(arry, scope){
|
311
326
|
var res = []; // new array
|
@@ -315,58 +330,64 @@ Ext.widgetMixIn = {
|
|
315
330
|
if (scope.actions[camelized]){
|
316
331
|
res.push(scope.actions[camelized]);
|
317
332
|
} else {
|
318
|
-
// if there's no action with this name, maybe it's a separator or
|
333
|
+
// if there's no action with this name, maybe it's a separator or text or whatever
|
319
334
|
res.push(o);
|
320
335
|
}
|
321
336
|
} else if (Netzke.isObject(o)) {
|
322
337
|
// look inside the objects...
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
338
|
+
if (o.menu) {
|
339
|
+
// ... and recursively process nested menus
|
340
|
+
o.menu = this.normalizeMenuItems(o.menu, scope);
|
341
|
+
} else if (o.handler && Ext.isFunction(scope[o.handler.camelize(true)])) {
|
342
|
+
// This button config has a handler specified as string - replace it with reference to a real function if it exists
|
343
|
+
o.handler = scope[o.handler.camelize(true)];
|
328
344
|
}
|
329
345
|
res.push(o);
|
330
346
|
}
|
331
347
|
}, this);
|
348
|
+
|
349
|
+
delete arry;
|
350
|
+
|
332
351
|
return res;
|
333
352
|
},
|
334
353
|
|
335
354
|
|
336
|
-
//
|
355
|
+
// Code run before calling Ext's constructor - normalizing config to provide Netzke additional functionality
|
337
356
|
commonBeforeConstructor : function(config){
|
338
|
-
this.
|
339
|
-
|
340
|
-
//
|
341
|
-
|
342
|
-
config.api.push('load_aggregatee_with_cache'); // all netzke widgets get this API
|
343
|
-
Ext.each(config.api, function(intp){
|
357
|
+
// Dynamically create methods for api points, so that we could later call them like: this.myApiMethod()
|
358
|
+
var apiPoints = config.netzkeApi || [];
|
359
|
+
apiPoints.push('load_aggregatee_with_cache'); // all netzke widgets get this API point
|
360
|
+
Ext.each(apiPoints, function(intp){
|
344
361
|
this[intp.camelize(true)] = function(args, callback, scope){ this.callServer(intp, args, callback, scope); }
|
345
362
|
}, this);
|
346
363
|
|
347
|
-
//
|
364
|
+
// This will contain Ext.Action instances
|
365
|
+
this.actions = {};
|
366
|
+
|
367
|
+
// Create Ext.Action instances based on config.actions
|
348
368
|
if (config.actions) {
|
349
|
-
this.testActions = {};
|
350
369
|
for (var name in config.actions) {
|
351
370
|
// Create an event for each action (so that higher-level widgets could interfere)
|
352
371
|
this.addEvents(name+'click');
|
353
372
|
|
354
373
|
// Configure the action
|
355
374
|
var actionConfig = config.actions[name];
|
356
|
-
actionConfig.
|
375
|
+
actionConfig.customHandler = actionConfig.handler || actionConfig.fn; //DEPRECATED: .fn is kept for backward compatibility, preferred way is to specify handler
|
376
|
+
actionConfig.handler = this.actionHandler.createDelegate(this); // ! this is the "wrapper-handler", which is common for all actions!
|
357
377
|
actionConfig.name = name;
|
358
378
|
this.actions[name] = new Ext.Action(actionConfig);
|
359
379
|
}
|
360
|
-
|
361
|
-
config.bbar = config.bbar && this.normalizeMenuItems(config.bbar, this);
|
362
|
-
config.tbar = config.tbar && this.normalizeMenuItems(config.tbar, this);
|
363
|
-
config.menu = config.menu && this.normalizeMenuItems(config.menu, this);
|
364
|
-
config.contextMenu = config.contextMenu && this.normalizeMenuItems(config.contextMenu, this);
|
365
380
|
|
366
381
|
// TODO: need to rethink this action related stuff
|
367
382
|
config.actions = this.actions;
|
368
|
-
|
369
383
|
}
|
384
|
+
|
385
|
+
config.bbar = config.bbar && this.normalizeMenuItems(config.bbar, this);
|
386
|
+
config.tbar = config.tbar && this.normalizeMenuItems(config.tbar, this);
|
387
|
+
config.fbar = config.fbar && this.normalizeMenuItems(config.fbar, this);
|
388
|
+
config.contextMenu = config.contextMenu && this.normalizeMenuItems(config.contextMenu, this);
|
389
|
+
|
390
|
+
config.menu = config.menu && this.normalizeMenuItems(config.menu, this);
|
370
391
|
|
371
392
|
// Normalize tools
|
372
393
|
if (config.tools) {
|
@@ -382,7 +403,13 @@ Ext.widgetMixIn = {
|
|
382
403
|
}
|
383
404
|
|
384
405
|
// Set title
|
385
|
-
if (!config.title)
|
406
|
+
if (!config.title) {
|
407
|
+
config.title = config.id.humanize();
|
408
|
+
} else {
|
409
|
+
if (config.mode === "config") {
|
410
|
+
config.title = config.title + ' (' + config.id + ')';
|
411
|
+
}
|
412
|
+
}
|
386
413
|
},
|
387
414
|
|
388
415
|
// At this moment component is fully initializied
|
@@ -486,17 +513,30 @@ Ext.override(Ext.Container, {
|
|
486
513
|
return this.items ? this.items.get(0) : null; // need this check in case when the container is not yet rendered, like an inactive tab in the TabPanel
|
487
514
|
},
|
488
515
|
|
516
|
+
// Remove the child
|
489
517
|
removeChild : function(){
|
490
518
|
this.remove(this.getWidget());
|
491
519
|
},
|
492
520
|
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
521
|
+
// Given a scoped class name, returns the actual class, e.g.: "Netzke.GridPanel" => Netzke.classes.Netzke.GridPanel
|
522
|
+
classifyScopedName : function(n){
|
523
|
+
var klass = Netzke.classes;
|
524
|
+
Ext.each(n.split("."), function(s){
|
525
|
+
klass = klass[s];
|
526
|
+
});
|
527
|
+
return klass;
|
528
|
+
},
|
497
529
|
|
498
|
-
|
499
|
-
|
500
|
-
this.
|
530
|
+
// Instantiates an aggregatee by its config. If it appears to be a window, shows it instead of adding as item.
|
531
|
+
instantiateChild : function(config){
|
532
|
+
var klass = this.classifyScopedName(config.scopedClassName);
|
533
|
+
var instance = new klass(config);
|
534
|
+
if (instance.isXType("netzkewindow")) {
|
535
|
+
instance.show();
|
536
|
+
} else {
|
537
|
+
this.remove(this.getWidget()); // first delete previous widget
|
538
|
+
this.add(instance);
|
539
|
+
this.doLayout();
|
540
|
+
}
|
501
541
|
}
|
502
542
|
});
|
data/lib/netzke/base.rb
CHANGED
@@ -38,138 +38,129 @@ module Netzke
|
|
38
38
|
# netzke :form_panel,
|
39
39
|
# :data_class_name => "User" # FormPanel specific option
|
40
40
|
class Base
|
41
|
+
extend ActiveSupport::Memoizable
|
42
|
+
|
41
43
|
include Netzke::BaseJs # javascript (client-side)
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
}
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
45
|
+
attr_accessor :parent, :name, :global_id, :permissions, :session
|
46
|
+
|
47
|
+
# Class-level Netzke::Base configuration. The defaults also get specified here.
|
48
|
+
def self.config
|
49
|
+
set_default_config({
|
50
|
+
# which javascripts and stylesheets must get included at the initial load (see netzke-core.rb)
|
51
|
+
:javascripts => [],
|
52
|
+
:stylesheets => [],
|
53
|
+
|
54
|
+
:persistent_config_manager => "NetzkePreference",
|
55
|
+
:ext_location => defined?(RAILS_ROOT) && "#{RAILS_ROOT}/public/extjs",
|
56
|
+
:default_config => {
|
57
|
+
:persistent_config => true
|
58
|
+
}
|
59
|
+
})
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.set_default_config(c) #:nodoc:
|
63
|
+
@@config ||= {}
|
64
|
+
@@config[self.name] ||= c
|
65
|
+
end
|
66
|
+
|
67
|
+
# Override class-level defaults specified in <tt>Netzke::Base.config</tt>.
|
68
|
+
# E.g. in config/initializers/netzke-config.rb:
|
69
|
+
#
|
70
|
+
# Netzke::GridPanel.configure :default_config => {:persistent_config => true}
|
71
|
+
def self.configure(*args)
|
72
|
+
if args.first.is_a?(Symbol)
|
73
|
+
config[args.first] = args.last
|
74
|
+
else
|
75
|
+
# first arg is hash
|
76
|
+
config.deep_merge!(args.first)
|
68
77
|
end
|
69
78
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
@@user ||= nil
|
80
|
-
end
|
81
|
-
|
82
|
-
def user=(user)
|
83
|
-
@@user = user
|
84
|
-
end
|
85
|
-
|
86
|
-
# Access to controller sessions
|
87
|
-
def session
|
88
|
-
@@session ||= {}
|
89
|
-
end
|
79
|
+
# widget may implement some kind of control for configuration consistency
|
80
|
+
enforce_config_consistency if respond_to?(:enforce_config_consistency)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Short widget class name, e.g.:
|
84
|
+
# Netzke::Module::SomeWidget => Module::SomeWidget
|
85
|
+
def self.short_widget_class_name
|
86
|
+
self.name.sub("Netzke::", "")
|
87
|
+
end
|
90
88
|
|
91
|
-
|
92
|
-
|
93
|
-
|
89
|
+
# Access to controller sessions
|
90
|
+
def self.session
|
91
|
+
@@session ||= {}
|
92
|
+
end
|
94
93
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
99
|
-
|
100
|
-
# called by controller at the moment of logout
|
101
|
-
def logout
|
102
|
-
session[:_netzke_next_request_is_first_after_logout] = true
|
103
|
-
end
|
94
|
+
def self.session=(s)
|
95
|
+
@@session = s
|
96
|
+
end
|
104
97
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
98
|
+
# Should be called by session controller at the moment of successfull login
|
99
|
+
def self.login
|
100
|
+
session[:_netzke_next_request_is_first_after_login] = true
|
101
|
+
end
|
102
|
+
|
103
|
+
# Should be called by session controller at the moment of logout
|
104
|
+
def self.logout
|
105
|
+
session[:_netzke_next_request_is_first_after_logout] = true
|
106
|
+
end
|
107
|
+
|
108
|
+
# Declare connection points between client side of a widget and its server side. For example:
|
109
|
+
#
|
110
|
+
# api :reset_data
|
111
|
+
#
|
112
|
+
# will provide JavaScript side with a method <tt>resetData</tt> that will result in a call to Ruby
|
113
|
+
# method <tt>reset_data</tt>, e.g.:
|
114
|
+
#
|
115
|
+
# this.resetData({hard:true});
|
116
|
+
#
|
117
|
+
# See netzke-basepack's GridPanel for an example.
|
118
|
+
def self.api(*api_points)
|
119
|
+
apip = read_inheritable_attribute(:api_points) || []
|
120
|
+
api_points.each{|p| apip << p}
|
121
|
+
write_inheritable_attribute(:api_points, apip)
|
122
|
+
|
123
|
+
# It may be needed later for security
|
124
|
+
api_points.each do |apip|
|
125
|
+
module_eval <<-END, __FILE__, __LINE__
|
126
|
+
def api_#{apip}(*args)
|
127
|
+
#{apip}(*args).to_nifty_json
|
125
128
|
end
|
129
|
+
# FIXME: commented out because otherwise ColumnOperations stop working
|
130
|
+
# def #{apip}(*args)
|
131
|
+
# flash :warning => "API point '#{apip}' is not implemented for widget '#{short_widget_class_name}'"
|
132
|
+
# {:flash => @flash}
|
133
|
+
# end
|
134
|
+
END
|
126
135
|
end
|
136
|
+
end
|
127
137
|
|
128
|
-
|
129
|
-
read_inheritable_attribute(:api_points)
|
130
|
-
end
|
131
|
-
|
132
|
-
# returns an instance of a widget defined in the config
|
133
|
-
def instance_by_config(config)
|
134
|
-
widget_class = "Netzke::#{config[:widget_class_name]}".constantize
|
135
|
-
widget_class.new(config)
|
136
|
-
end
|
137
|
-
|
138
|
-
# persistent_config and layout manager classes
|
139
|
-
def persistent_config_manager_class
|
140
|
-
Netzke::Base.config[:persistent_config_manager].try(:constantize)
|
141
|
-
rescue NameError
|
142
|
-
nil
|
143
|
-
end
|
138
|
+
api :load_aggregatee_with_cache # every widget gets this api
|
144
139
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
if persistent_config_manager_class.nil?
|
149
|
-
{}
|
150
|
-
else
|
151
|
-
persistent_config_manager_class
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
private
|
156
|
-
def set_default_config(c)
|
157
|
-
@@config ||= {}
|
158
|
-
@@config[self.name] ||= c
|
159
|
-
end
|
160
|
-
|
140
|
+
# Array of API-points specified with <tt>Netzke::Base.api</tt> method
|
141
|
+
def self.api_points
|
142
|
+
read_inheritable_attribute(:api_points)
|
161
143
|
end
|
162
|
-
extend ClassMethods
|
163
144
|
|
164
|
-
#
|
165
|
-
def
|
166
|
-
|
145
|
+
# Instance of widget by config
|
146
|
+
def self.instance_by_config(config)
|
147
|
+
widget_class = "Netzke::#{config[:widget_class_name]}".constantize
|
148
|
+
widget_class.new(config)
|
167
149
|
end
|
168
150
|
|
169
|
-
|
170
|
-
|
171
|
-
|
151
|
+
# Persistent config manager class
|
152
|
+
def self.persistent_config_manager_class
|
153
|
+
Netzke::Base.config[:persistent_config_manager].try(:constantize)
|
154
|
+
rescue NameError
|
155
|
+
nil
|
156
|
+
end
|
172
157
|
|
158
|
+
# Return persistent config class
|
159
|
+
# def self.persistent_config
|
160
|
+
# # if the class is not present, fake it (it will not store anything, and always return nil)
|
161
|
+
# persistent_config_manager_class || {}
|
162
|
+
# end
|
163
|
+
|
173
164
|
# Widget initialization process
|
174
165
|
# * the config hash is available to the widget after the "super" call in the initializer
|
175
166
|
# * override/add new default configuration options into the "default_config" method
|
@@ -179,49 +170,78 @@ module Netzke
|
|
179
170
|
@passed_config = config # configuration passed at the moment of instantiation
|
180
171
|
@parent = parent
|
181
172
|
@name = config[:name].nil? ? short_widget_class_name.underscore : config[:name].to_s
|
182
|
-
@
|
173
|
+
@global_id = parent.nil? ? @name : "#{parent.global_id}__#{@name}"
|
183
174
|
@flash = []
|
184
175
|
end
|
185
176
|
|
186
|
-
#
|
187
|
-
|
188
|
-
|
189
|
-
res = []
|
190
|
-
self.each_pair do |k,v|
|
191
|
-
if v.is_a?(Hash)
|
192
|
-
res += v.flatten(k)
|
193
|
-
else
|
194
|
-
res << {
|
195
|
-
:name => ((preffix.to_s.empty? ? "" : preffix.to_s + "__") + k.to_s).to_sym,
|
196
|
-
:value => v,
|
197
|
-
:type => (["TrueClass", "FalseClass"].include?(v.class.name) ? 'Boolean' : v.class.name).to_sym
|
198
|
-
}
|
199
|
-
end
|
200
|
-
end
|
201
|
-
res
|
202
|
-
end
|
203
|
-
end
|
177
|
+
#
|
178
|
+
# Configuration
|
179
|
+
#
|
204
180
|
|
181
|
+
# Default config - before applying any passed configuration
|
205
182
|
def default_config
|
206
|
-
self.class.config[:default_config].nil? ? {} : {}.merge
|
183
|
+
self.class.config[:default_config].nil? ? {} : {}.merge(self.class.config[:default_config])
|
184
|
+
end
|
185
|
+
|
186
|
+
# Static, hardcoded config. Consists of default values merged with config that was passed during instantiation
|
187
|
+
def initial_config
|
188
|
+
default_config.deep_merge(@passed_config)
|
189
|
+
end
|
190
|
+
memoize :initial_config
|
191
|
+
|
192
|
+
# Config that is not overwritten by parents and sessions
|
193
|
+
def independent_config
|
194
|
+
initial_config.deep_merge(persistent_config_hash)
|
207
195
|
end
|
196
|
+
memoize :independent_config
|
208
197
|
|
209
|
-
#
|
198
|
+
# If the widget has persistent config in its disposal
|
199
|
+
def persistent_config_enabled?
|
200
|
+
!persistent_config_manager_class.nil? && initial_config[:persistent_config]
|
201
|
+
end
|
202
|
+
|
203
|
+
# Store some setting in the database as if it was a hash, e.g.:
|
204
|
+
# persistent_config["window.size"] = 100
|
205
|
+
# persistent_config["window.size"] => 100
|
206
|
+
# This method is user-aware
|
207
|
+
def persistent_config(global = false)
|
208
|
+
if persistent_config_enabled? || global
|
209
|
+
config_class = self.class.persistent_config_manager_class
|
210
|
+
config_class.widget_name = global ? nil : persistent_config_id # pass to the config class our unique name
|
211
|
+
config_class
|
212
|
+
else
|
213
|
+
# if we can't use presistent config, all the calls to it will always return nil,
|
214
|
+
# and the "="-operation will be ignored
|
215
|
+
logger.debug "==> NETZKE: no persistent config is set up for widget '#{global_id}'"
|
216
|
+
{}
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# A string which will identify the persistent config records for this widget
|
221
|
+
def persistent_config_id #:nodoc:
|
222
|
+
initial_config[:persistent_config_id] || global_id
|
223
|
+
end
|
224
|
+
|
225
|
+
def update_persistent_ext_config(hsh)
|
226
|
+
current_config = persistent_config[:ext_config] || {}
|
227
|
+
current_config.deep_merge!(hsh.convert_keys{ |k| k.to_s }) # first, recursively stringify the keys
|
228
|
+
persistent_config[:ext_config] = current_config
|
229
|
+
end
|
230
|
+
|
231
|
+
# Resulting config that takes into account all possible ways to configure a widget. *Read only*.
|
232
|
+
# Translates into something like this:
|
233
|
+
# default_config.
|
234
|
+
# deep_merge(@passed_config).
|
235
|
+
# deep_merge(persistent_config_hash).
|
236
|
+
# deep_merge(strong_parent_config).
|
237
|
+
# deep_merge(strong_session_config)
|
210
238
|
def config
|
211
|
-
|
212
|
-
# @config ||= default_config.
|
213
|
-
# deep_merge(@passed_config).
|
214
|
-
# deep_merge(persistent_config_hash).
|
215
|
-
# deep_merge(strong_parent_config).
|
216
|
-
# deep_merge(strong_session_config)
|
217
|
-
@config ||= independent_config.
|
218
|
-
deep_merge(strong_parent_config).
|
219
|
-
deep_merge(strong_session_config)
|
220
|
-
|
239
|
+
independent_config.deep_merge(strong_parent_config).deep_merge(strong_session_config)
|
221
240
|
end
|
241
|
+
memoize :config
|
222
242
|
|
223
243
|
def flat_config(key = nil)
|
224
|
-
fc = config.
|
244
|
+
fc = config.flatten_with_type
|
225
245
|
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
226
246
|
end
|
227
247
|
|
@@ -229,35 +249,33 @@ module Netzke
|
|
229
249
|
@strong_parent_config ||= parent.nil? ? {} : parent.strong_children_config
|
230
250
|
end
|
231
251
|
|
232
|
-
# Config that is not overwritten by parents and sessions
|
233
|
-
def independent_config
|
234
|
-
@independent_config ||= initial_config.deep_merge(persistent_config_hash)
|
235
|
-
end
|
236
|
-
|
237
252
|
def flat_independent_config(key = nil)
|
238
|
-
fc = independent_config.
|
253
|
+
fc = independent_config.flatten_with_type
|
239
254
|
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
240
255
|
end
|
241
256
|
|
242
257
|
def flat_default_config(key = nil)
|
243
|
-
fc = default_config.
|
258
|
+
fc = default_config.flatten_with_type
|
244
259
|
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
245
260
|
end
|
246
261
|
|
247
|
-
# Static, hardcoded config. Consists of default values merged with config that was passed during instantiation
|
248
|
-
def initial_config
|
249
|
-
@initial_config ||= default_config.deep_merge(@passed_config)
|
250
|
-
end
|
251
|
-
|
252
262
|
def flat_initial_config(key = nil)
|
253
|
-
fc = initial_config.
|
263
|
+
fc = initial_config.flatten_with_type
|
254
264
|
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
255
265
|
end
|
256
266
|
|
257
|
-
|
267
|
+
# Returns a hash built from all persistent config values for the current widget, following the double underscore
|
268
|
+
# naming convention. E.g., if we have the following persistent config pairs:
|
269
|
+
# enabled => true
|
270
|
+
# layout__width => 100
|
271
|
+
# layout__header__height => 20
|
272
|
+
#
|
273
|
+
# this method will return the following hash:
|
274
|
+
# {:enabled => true, :layout => {:width => 100, :header => {:height => 20}}}
|
275
|
+
def persistent_config_hash
|
258
276
|
return {} if !initial_config[:persistent_config]
|
259
277
|
|
260
|
-
prefs = NetzkePreference.find_all_for_widget(
|
278
|
+
prefs = NetzkePreference.find_all_for_widget(persistent_config_id)
|
261
279
|
res = {}
|
262
280
|
prefs.each do |p|
|
263
281
|
hsh_levels = p.name.split("__").map(&:to_sym)
|
@@ -273,13 +291,10 @@ module Netzke
|
|
273
291
|
# So we need to recursively merge it into the final result
|
274
292
|
res.deep_merge!(hsh_levels.first => anchor)
|
275
293
|
end
|
276
|
-
res
|
277
|
-
end
|
278
|
-
|
279
|
-
def persistent_config_hash
|
280
|
-
@persistent_config_hash ||= build_persistent_config_hash
|
294
|
+
res.convert_keys{ |k| k.to_sym } # recursively symbolize the keys
|
281
295
|
end
|
282
|
-
|
296
|
+
memoize :persistent_config_hash
|
297
|
+
|
283
298
|
def ext_config
|
284
299
|
config[:ext_config] || {}
|
285
300
|
end
|
@@ -313,7 +328,7 @@ module Netzke
|
|
313
328
|
end
|
314
329
|
|
315
330
|
def widget_session
|
316
|
-
session[
|
331
|
+
session[global_id] ||= {}
|
317
332
|
end
|
318
333
|
|
319
334
|
# Rails' logger
|
@@ -330,22 +345,6 @@ module Netzke
|
|
330
345
|
res.uniq
|
331
346
|
end
|
332
347
|
|
333
|
-
# Store some setting in the database as if it was a hash, e.g.:
|
334
|
-
# persistent_config["window.size"] = 100
|
335
|
-
# persistent_config["window.size"] => 100
|
336
|
-
# This method is user-aware
|
337
|
-
def persistent_config
|
338
|
-
if config[:persistent_config]
|
339
|
-
config_class = self.class.persistent_config
|
340
|
-
config_class.widget_name = id_name # pass to the config class our unique name
|
341
|
-
config_class
|
342
|
-
else
|
343
|
-
# if we can't use presistent config, all the calls to it will always return nil, and the "="-operation will be ignored
|
344
|
-
logger.debug "==> NETZKE: no persistent config is set up for widget '#{id_name}'"
|
345
|
-
{}
|
346
|
-
end
|
347
|
-
end
|
348
|
-
|
349
348
|
# 'Netzke::Grid' => 'Grid'
|
350
349
|
def short_widget_class_name
|
351
350
|
self.class.short_widget_class_name
|
@@ -383,7 +382,7 @@ module Netzke
|
|
383
382
|
|
384
383
|
def remove_aggregatee(aggr)
|
385
384
|
if config[:persistent_config]
|
386
|
-
persistent_config_manager_class.delete_all_for_widget("#{
|
385
|
+
persistent_config_manager_class.delete_all_for_widget("#{global_id}__#{aggr}")
|
387
386
|
end
|
388
387
|
aggregatees[aggr] = nil
|
389
388
|
end
|
@@ -403,9 +402,9 @@ module Netzke
|
|
403
402
|
name.to_s.split('__').each do |aggr|
|
404
403
|
aggr = aggr.to_sym
|
405
404
|
aggregatee_config = aggregator.aggregatees[aggr]
|
406
|
-
raise ArgumentError, "No aggregatee '#{aggr}' defined for widget '#{aggregator.
|
405
|
+
raise ArgumentError, "No aggregatee '#{aggr}' defined for widget '#{aggregator.global_id}'" if aggregatee_config.nil?
|
407
406
|
short_class_name = aggregatee_config[:widget_class_name]
|
408
|
-
raise ArgumentError, "No widget_class_name specified for aggregatee #{aggr} of #{aggregator.
|
407
|
+
raise ArgumentError, "No widget_class_name specified for aggregatee #{aggr} of #{aggregator.global_id}" if short_class_name.nil?
|
409
408
|
widget_class = "Netzke::#{short_class_name}".constantize
|
410
409
|
|
411
410
|
conf = weak_children_config.
|
@@ -431,7 +430,7 @@ module Netzke
|
|
431
430
|
end
|
432
431
|
|
433
432
|
def widget_action(action_name)
|
434
|
-
"#{@
|
433
|
+
"#{@global_id}__#{action_name}"
|
435
434
|
end
|
436
435
|
|
437
436
|
# called when the method_missing tries to processes a non-existing aggregatee
|
@@ -458,29 +457,54 @@ module Netzke
|
|
458
457
|
widget_session.clear
|
459
458
|
end
|
460
459
|
|
461
|
-
#
|
462
|
-
#
|
460
|
+
# Returns global id of a widget in the hierarchy, based on passed reference that follows
|
461
|
+
# the double-underscore notation. Referring to "parent" is allowed. If going to far up the hierarchy will
|
462
|
+
# result in <tt>nil</tt>, while referring to a non-existent aggregatee will simply provide an erroneous ID.
|
463
|
+
# Example:
|
464
|
+
# <tt>parent__parent__child__subchild</tt> will traverse the hierarchy 2 levels up, then going down to "child",
|
465
|
+
# and further to "subchild". If such a widget exists in the hierarchy, its global id will be returned, otherwise
|
466
|
+
# <tt>nil</tt> will be returned.
|
467
|
+
def global_id_by_reference(ref)
|
468
|
+
ref = ref.to_s
|
469
|
+
return parent && parent.global_id if ref == "parent"
|
470
|
+
substr = ref.sub(/^parent__/, "")
|
471
|
+
if substr == ref # there's no "parent__" in the beginning
|
472
|
+
return global_id + "__" + ref
|
473
|
+
else
|
474
|
+
return parent.global_id_by_reference(substr)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
# API: provides what is necessary for the browser to render a widget.
|
479
|
+
# <tt>params</tt> should contain:
|
480
|
+
# * <tt>:cache</tt> - an array of widget classes cached at the browser
|
481
|
+
# * <tt>:id</tt> - reference to the aggregatee
|
482
|
+
# * <tt>:container</tt> - Ext id of the container where in which the aggregatee will be rendered
|
463
483
|
def load_aggregatee_with_cache(params)
|
464
484
|
cache = ActiveSupport::JSON.decode(params.delete(:cache))
|
465
|
-
relative_widget_id = params.delete(:id).underscore
|
466
|
-
widget = aggregatee_instance(relative_widget_id)
|
485
|
+
relative_widget_id = params.delete(:id).underscore.to_sym
|
486
|
+
widget = aggregatees[relative_widget_id] && aggregatee_instance(relative_widget_id)
|
467
487
|
|
468
|
-
|
469
|
-
|
488
|
+
if widget
|
489
|
+
# inform the widget that it's being loaded
|
490
|
+
widget.before_load
|
470
491
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
492
|
+
[{
|
493
|
+
:js => widget.js_missing_code(cache),
|
494
|
+
:css => widget.css_missing_code(cache)
|
495
|
+
}, {
|
496
|
+
:render_widget_in_container => { # TODO: rename it
|
497
|
+
:container => params[:container],
|
498
|
+
:config => widget.js_config
|
499
|
+
}
|
500
|
+
}, {
|
501
|
+
:widget_loaded => {
|
502
|
+
:id => relative_widget_id
|
503
|
+
}
|
504
|
+
}]
|
505
|
+
else
|
506
|
+
{:feedback => "Couldn't load aggregatee '#{relative_widget_id}'"}
|
507
|
+
end
|
484
508
|
end
|
485
509
|
|
486
510
|
# Method dispatcher - instantiates an aggregatee and calls the method on it
|
data/lib/netzke/base_js.rb
CHANGED
@@ -30,7 +30,7 @@ module Netzke
|
|
30
30
|
res = {}
|
31
31
|
|
32
32
|
# Unique id of the widget
|
33
|
-
res.merge!(:id =>
|
33
|
+
res.merge!(:id => global_id)
|
34
34
|
|
35
35
|
# Recursively include configs of all non-late aggregatees, so that the widget can instantiate them
|
36
36
|
# in javascript immediately.
|
@@ -42,10 +42,11 @@ module Netzke
|
|
42
42
|
|
43
43
|
# Api (besides the default "load_aggregatee_with_cache" - JavaScript side already knows about it)
|
44
44
|
api_points = self.class.api_points.reject{ |p| p == :load_aggregatee_with_cache }
|
45
|
-
res.merge!(:
|
45
|
+
res.merge!(:netzke_api => api_points) unless api_points.empty?
|
46
46
|
|
47
47
|
# Widget class name. Needed for dynamic instantiation in javascript.
|
48
|
-
res.merge!(:widget_class_name => short_widget_class_name)
|
48
|
+
# res.merge!(:widget_class_name => short_widget_class_name)
|
49
|
+
res.merge!(:scoped_class_name => self.class.js_scoped_class_name)
|
49
50
|
|
50
51
|
# Actions, toolbars and menus
|
51
52
|
# tools && res.merge!(:tools => tools)
|
@@ -56,8 +57,8 @@ module Netzke
|
|
56
57
|
res[:persistent_config] = persistent_config_enabled?
|
57
58
|
|
58
59
|
# Merge with all config options passed as hash to config[:ext_config]
|
60
|
+
logger.debug "!!! ext_config: #{ext_config.inspect}\n"
|
59
61
|
res.merge!(ext_config)
|
60
|
-
|
61
62
|
|
62
63
|
res
|
63
64
|
end
|
@@ -84,12 +85,18 @@ module Netzke
|
|
84
85
|
|
85
86
|
# instantiating
|
86
87
|
def js_widget_instance
|
87
|
-
%Q{var #{name.jsonify} = new
|
88
|
+
%Q{var #{name.jsonify} = new #{self.class.js_full_class_name}(#{js_config.to_nifty_json});}
|
88
89
|
end
|
89
90
|
|
90
91
|
# rendering
|
91
92
|
def js_widget_render
|
92
|
-
%Q{
|
93
|
+
%Q{
|
94
|
+
if (#{name.jsonify}.isXType("netzkewindow")) {
|
95
|
+
#{name.jsonify}.show();
|
96
|
+
} else {
|
97
|
+
#{name.jsonify}.render("#{name.to_s.split('_').join('-')}-div");
|
98
|
+
}
|
99
|
+
}
|
93
100
|
end
|
94
101
|
|
95
102
|
# container for rendering
|
@@ -121,17 +128,39 @@ module Netzke
|
|
121
128
|
"Ext.Panel"
|
122
129
|
end
|
123
130
|
|
124
|
-
#
|
131
|
+
# Properties (including methods) that will be used to extend the functionality of (Ext) JS-class specified in js_base_class
|
125
132
|
def js_extend_properties
|
126
133
|
{}
|
127
134
|
end
|
135
|
+
|
136
|
+
# Returns the scope of this widget,
|
137
|
+
# e.g. "Netzke.GridPanelLib"
|
138
|
+
def js_scope
|
139
|
+
js_full_class_name.split(".")[0..-2].join(".")
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns the name of the JavaScript class for this widget, including the scopes,
|
143
|
+
# e.g.: "Netzke.GridPanelLib.RecordFormWindow"
|
144
|
+
def js_scoped_class_name
|
145
|
+
name.gsub("::", ".")
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns the full name of the JavaScript class, including the scopes *and* the common scope, which is
|
149
|
+
# Netzke.classes.
|
150
|
+
# E.g.: "Netzke.classes.Netzke.GridPanelLib.RecordFormWindow"
|
151
|
+
def js_full_class_name
|
152
|
+
"Netzke.classes." + js_scoped_class_name
|
153
|
+
end
|
154
|
+
|
155
|
+
# Builds this widget's xtype
|
156
|
+
# E.g.: netzkewindow, netzkegridpanel
|
157
|
+
def js_xtype
|
158
|
+
name.gsub("::", "").downcase
|
159
|
+
end
|
128
160
|
|
129
161
|
# widget's menus
|
130
162
|
def js_menus; []; end
|
131
163
|
|
132
|
-
# items
|
133
|
-
# def js_items; null; end
|
134
|
-
|
135
164
|
# are we using JS inheritance? for now, if js_base_class is a Netzke class - yes
|
136
165
|
def js_inheritance?
|
137
166
|
superclass != Netzke::Base
|
@@ -143,30 +172,39 @@ module Netzke
|
|
143
172
|
if js_inheritance?
|
144
173
|
# In case of using javascript inheritance, little needs to be done
|
145
174
|
<<-END_OF_JAVASCRIPT
|
175
|
+
// Define the scope
|
176
|
+
Ext.ns("#{js_scope}");
|
146
177
|
// Create the class
|
147
|
-
|
148
|
-
|
178
|
+
#{js_full_class_name} = function(config){
|
179
|
+
#{js_full_class_name}.superclass.constructor.call(this, config);
|
149
180
|
};
|
150
181
|
// Extend it with the class that we inherit from, and mix in js_extend_properties
|
151
|
-
Ext.extend(
|
182
|
+
Ext.extend(#{js_full_class_name}, #{superclass.js_full_class_name}, Ext.applyIf(#{js_extend_properties.to_nifty_json}, Ext.widgetMixIn));
|
183
|
+
// Register xtype
|
184
|
+
Ext.reg("#{js_xtype}", #{js_full_class_name});
|
185
|
+
|
152
186
|
END_OF_JAVASCRIPT
|
187
|
+
|
153
188
|
else
|
154
189
|
js_add_menus = "this.addMenus(#{js_menus.to_nifty_json});" unless js_menus.empty?
|
155
190
|
<<-END_OF_JAVASCRIPT
|
191
|
+
// Define the scope
|
192
|
+
Ext.ns("#{js_scope}");
|
156
193
|
// Constructor
|
157
|
-
|
194
|
+
#{js_full_class_name} = function(config){
|
158
195
|
// Do all the initializations that every Netzke widget should do: create methods for API-points,
|
159
196
|
// process actions, tools, toolbars
|
160
197
|
this.commonBeforeConstructor(config);
|
161
|
-
|
162
198
|
// Call the constructor of the inherited class
|
163
|
-
|
164
|
-
|
199
|
+
#{js_full_class_name}.superclass.constructor.call(this, config);
|
165
200
|
// What every widget should do after calling the constructor of the inherited class, like
|
166
201
|
// setting extra events
|
167
202
|
this.commonAfterConstructor(config);
|
168
203
|
};
|
169
|
-
Ext.extend(
|
204
|
+
Ext.extend(#{js_full_class_name}, #{js_base_class}, Ext.applyIf(#{js_extend_properties.to_nifty_json}, Ext.widgetMixIn));
|
205
|
+
// Register xtype
|
206
|
+
Ext.reg("#{js_xtype}", #{js_full_class_name});
|
207
|
+
|
170
208
|
END_OF_JAVASCRIPT
|
171
209
|
end
|
172
210
|
end
|
@@ -9,8 +9,6 @@ module Netzke
|
|
9
9
|
Netzke::Base.session = session
|
10
10
|
session[:netzke_user_id] = defined?(current_user) ? current_user.try(:id) : nil
|
11
11
|
|
12
|
-
Netzke::Base.user = defined?(current_user) ? current_user : nil # for backward compatibility (TODO: eliminate the need for this)
|
13
|
-
|
14
12
|
# set netzke_just_logged_in and netzke_just_logged_out states (may be used by Netzke widgets)
|
15
13
|
if session[:_netzke_next_request_is_first_after_login]
|
16
14
|
session[:netzke_just_logged_in] = true
|
@@ -44,10 +42,8 @@ module Netzke
|
|
44
42
|
|
45
43
|
# instantiate the server part of the widget
|
46
44
|
widget_instance = widget_class.new(self.class.widget_config_storage[widget])
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
render :text => widget_instance.send(api_action, params)
|
45
|
+
|
46
|
+
render :text => widget_instance.send(api_action, params), :layout => false
|
51
47
|
end
|
52
48
|
end
|
53
49
|
end
|
data/lib/netzke/core_ext.rb
CHANGED
@@ -40,6 +40,24 @@ class Hash
|
|
40
40
|
h
|
41
41
|
end
|
42
42
|
end
|
43
|
+
|
44
|
+
# add flatten_with_type method to Hash
|
45
|
+
def flatten_with_type(preffix = "")
|
46
|
+
res = []
|
47
|
+
self.each_pair do |k,v|
|
48
|
+
name = ((preffix.to_s.empty? ? "" : preffix.to_s + "__") + k.to_s).to_sym
|
49
|
+
if v.is_a?(Hash)
|
50
|
+
res += v.flatten_with_type(name)
|
51
|
+
else
|
52
|
+
res << {
|
53
|
+
:name => name,
|
54
|
+
:value => v,
|
55
|
+
:type => (["TrueClass", "FalseClass"].include?(v.class.name) ? 'Boolean' : v.class.name).to_sym
|
56
|
+
}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
res
|
60
|
+
end
|
43
61
|
|
44
62
|
# Javascrit-like access to Hash values
|
45
63
|
def method_missing(method, *args)
|
data/test/unit/core_ext_test.rb
CHANGED
@@ -40,27 +40,31 @@ class CoreExtTest < ActiveSupport::TestCase
|
|
40
40
|
assert_equal({:aB => 1, "cD" => [[1, {:eF => "stay_same"}], {"literal_symbol" => :should_not_change, "literal_string".l => "also_should_not"}]}, {:a_b => 1, "c_d" => [[1, {:e_f => "stay_same"}], {:literal_symbol.l => :should_not_change, "literal_string".l => "also_should_not"}]}.jsonify)
|
41
41
|
end
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
43
|
+
test "flatten_with_type" do
|
44
|
+
test_flatten_with_type = {
|
45
|
+
:one => 1,
|
46
|
+
:two => 2.5,
|
47
|
+
:three => {
|
48
|
+
:four => true,
|
49
|
+
:five => {
|
50
|
+
:six => "a string"
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}.flatten_with_type
|
54
|
+
|
55
|
+
assert_equal(4, test_flatten_with_type.size)
|
56
|
+
|
57
|
+
test_flatten_with_type.each do |i|
|
58
|
+
assert([{
|
59
|
+
:name => :one, :value => 1, :type => :Fixnum
|
60
|
+
},{
|
61
|
+
:name => :two, :value => 2.5, :type => :Float
|
62
|
+
},{
|
63
|
+
:name => :three__four, :value => true, :type => :Boolean
|
64
|
+
},{
|
65
|
+
:name => :three__five__six, :value => "a string", :type => :String
|
66
|
+
}].include?(i))
|
67
|
+
end
|
68
|
+
end
|
65
69
|
|
66
70
|
end
|
@@ -43,6 +43,14 @@ module Netzke
|
|
43
43
|
end
|
44
44
|
|
45
45
|
class DeepNestedWidget < Base
|
46
|
+
def initial_aggregatees
|
47
|
+
{
|
48
|
+
:nested => {:widget_class_name => "VeryDeepNestedWidget"}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class VeryDeepNestedWidget < Base
|
46
54
|
end
|
47
55
|
|
48
56
|
class JsInheritanceWidget < Widget
|
@@ -90,31 +98,22 @@ class NetzkeCoreTest < ActiveSupport::TestCase
|
|
90
98
|
assert_kind_of DeepNestedWidget, deep_nested_widget
|
91
99
|
|
92
100
|
# check the internal names of aggregation instances
|
93
|
-
assert_equal 'my_widget', widget.
|
94
|
-
assert_equal 'my_widget__nested_one', nested_widget_one.
|
95
|
-
assert_equal 'my_widget__nested_two', nested_widget_two.
|
96
|
-
assert_equal 'my_widget__nested_two__nested', deep_nested_widget.
|
101
|
+
assert_equal 'my_widget', widget.global_id
|
102
|
+
assert_equal 'my_widget__nested_one', nested_widget_one.global_id
|
103
|
+
assert_equal 'my_widget__nested_two', nested_widget_two.global_id
|
104
|
+
assert_equal 'my_widget__nested_two__nested', deep_nested_widget.global_id
|
97
105
|
end
|
98
106
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
# widget = Widget.new(:prohibit => [:read, :update])
|
110
|
-
# assert_equal({:read => false, :update => false}, widget.permissions)
|
111
|
-
#
|
112
|
-
# widget = Widget.new(:prohibit => :all, :allow => :read)
|
113
|
-
# assert_equal({:read => true, :update => false}, widget.permissions)
|
114
|
-
#
|
115
|
-
# widget = Widget.new(:prohibit => :all, :allow => [:read, :update])
|
116
|
-
# assert_equal({:read => true, :update => true}, widget.permissions)
|
117
|
-
# end
|
107
|
+
test "global_id_by_reference" do
|
108
|
+
w = Widget.new(:name => "a_widget")
|
109
|
+
deep_nested_widget = w.aggregatee_instance(:nested_two__nested)
|
110
|
+
assert_equal("a_widget__nested_two", deep_nested_widget.global_id_by_reference(:parent))
|
111
|
+
assert_equal("a_widget", deep_nested_widget.global_id_by_reference(:parent__parent))
|
112
|
+
assert_equal("a_widget__nested_one", deep_nested_widget.global_id_by_reference(:parent__parent__nested_one))
|
113
|
+
assert_equal("a_widget__nested_two__nested__nested", deep_nested_widget.global_id_by_reference(:nested))
|
114
|
+
assert_equal("a_widget__nested_two__nested__non_existing", deep_nested_widget.global_id_by_reference(:non_existing))
|
115
|
+
assert_nil(deep_nested_widget.global_id_by_reference(:parent__parent__parent)) # too far up
|
116
|
+
end
|
118
117
|
|
119
118
|
test "default config" do
|
120
119
|
widget = Widget.new
|
@@ -145,8 +144,8 @@ class NetzkeCoreTest < ActiveSupport::TestCase
|
|
145
144
|
|
146
145
|
test "js inheritance" do
|
147
146
|
widget = JsInheritanceWidget.new
|
148
|
-
assert(widget.js_missing_code.index("
|
149
|
-
assert(widget.js_missing_code.index("
|
147
|
+
assert(widget.js_missing_code.index("Netzke.classes.Netzke.JsInheritanceWidget"))
|
148
|
+
assert(widget.js_missing_code.index("Netzke.classes.Netzke.Widget"))
|
150
149
|
end
|
151
150
|
|
152
151
|
test "class-level configuration" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: netzke-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergei Kozlov
|
@@ -9,11 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-11-08 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description:
|
16
|
+
description: Allows building ExtJS/Rails reusable code in a DRY way
|
17
17
|
email: sergei@playcode.nl
|
18
18
|
executables: []
|
19
19
|
|
@@ -31,7 +31,6 @@ files:
|
|
31
31
|
- README.rdoc
|
32
32
|
- Rakefile
|
33
33
|
- TODO
|
34
|
-
- VERSION
|
35
34
|
- autotest/discover.rb
|
36
35
|
- generators/netzke_core/USAGE
|
37
36
|
- generators/netzke_core/netzke_core_generator.rb
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.4.4
|