netzke-core 0.2.4 → 0.2.6
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.
- data/CHANGELOG +13 -0
- data/Manifest +2 -1
- data/README.mdown +3 -2
- data/javascripts/core.js +46 -25
- data/lib/app/models/netzke_layout.rb +13 -6
- data/lib/netzke/base.rb +41 -19
- data/lib/netzke/base_extras/interface.rb +11 -0
- data/lib/netzke/base_extras/js_builder.rb +197 -0
- data/lib/netzke/core_ext.rb +6 -2
- data/lib/netzke/feedback_ghost.rb +32 -32
- data/lib/netzke-core.rb +2 -2
- data/netzke-core.gemspec +4 -4
- data/test/netzke_core_test.rb +22 -4
- metadata +6 -4
- data/lib/netzke/js_class_builder.rb +0 -174
data/CHANGELOG
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
v0.2.6
|
2
|
+
FeedackGhost is now capable of displaying multiple flash messages.
|
3
|
+
Dependencies slightly refactored.
|
4
|
+
An informative exception added to Base#aggregatee_instance.
|
5
|
+
JS-level inheritance enabled.
|
6
|
+
Work-around for the problem with Ext 2.2.1 in loadWidget.
|
7
|
+
Events "<action_id>click" added to the widgets along with the actions.
|
8
|
+
aggregatee_missing method added to Netzke::Base - called when a non-existing aggregate of a widget is tried to be invoked
|
9
|
+
Code readability improvements.
|
10
|
+
|
11
|
+
v0.2.5
|
12
|
+
Minor code restructuring.
|
13
|
+
|
1
14
|
v0.2.4
|
2
15
|
Some minor improvements.
|
3
16
|
|
data/Manifest
CHANGED
@@ -12,10 +12,11 @@ lib/app/models/netzke_layout.rb
|
|
12
12
|
lib/app/models/netzke_preference.rb
|
13
13
|
lib/netzke/action_view_ext.rb
|
14
14
|
lib/netzke/base.rb
|
15
|
+
lib/netzke/base_extras/interface.rb
|
16
|
+
lib/netzke/base_extras/js_builder.rb
|
15
17
|
lib/netzke/controller_extensions.rb
|
16
18
|
lib/netzke/core_ext.rb
|
17
19
|
lib/netzke/feedback_ghost.rb
|
18
|
-
lib/netzke/js_class_builder.rb
|
19
20
|
lib/netzke/routing.rb
|
20
21
|
lib/netzke-core.rb
|
21
22
|
lib/vendor/facets/hash/recursive_merge.rb
|
data/README.mdown
CHANGED
@@ -4,9 +4,10 @@ Create Ext JS + Rails reusable components (widgets) with minimum effort.
|
|
4
4
|
Note that if you would like to modify this code or experiment with it, you may be better off cloning this project into your app's vendor/plugin directory - it will then behave as a Rails plugin.
|
5
5
|
|
6
6
|
# Example
|
7
|
+
See the introduction to the Netzke framework at http://github.com/skozlov/netzke/tree/master
|
7
8
|
|
8
|
-
|
9
|
+
The tutorials: http://blog.writelesscode.com
|
9
10
|
|
10
|
-
Also see the netzke-basepack project.
|
11
|
+
Also see the netzke-basepack project: http://github.com/skozlov/netzke-basepack/tree/master
|
11
12
|
|
12
13
|
Copyright (c) 2008-2009 Sergei Kozlov, released under the MIT license
|
data/javascripts/core.js
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
Ext.BLANK_IMAGE_URL = "/extjs/resources/images/default/s.gif";
|
2
|
-
Ext.componentCache = {};
|
3
|
-
|
4
2
|
Ext.namespace('Ext.netzke');
|
3
|
+
Ext.netzke.cache = {};
|
5
4
|
|
6
5
|
// to comply with Rails' forgery protection
|
7
6
|
Ext.Ajax.extraParams = {
|
@@ -48,18 +47,24 @@ Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
|
|
48
47
|
|
49
48
|
// Methods common to all widget classes
|
50
49
|
Ext.widgetMixIn = {
|
51
|
-
|
52
|
-
this.
|
53
|
-
// this.app = Ext.getCmp('application');
|
54
|
-
if (config.tools) Ext.each(config.tools, function(i){i.on.click = this[i.on.click].createDelegate(this)}, this);
|
55
|
-
if (config.actions) Ext.each(config.actions, function(i){i.handler = this[i.handler].createDelegate(this);}, this);
|
50
|
+
actionHandler : function(action){
|
51
|
+
if (this.fireEvent(action.handlerName+'click', action)) this[action.handlerName](action);
|
56
52
|
},
|
57
53
|
|
58
|
-
|
54
|
+
widgetInit : function(config){
|
55
|
+
this.app = Ext.getCmp('feedback_ghost');
|
56
|
+
if (config.tools) Ext.each(config.tools, function(i){
|
57
|
+
i.on.click = this[i.on.click].createDelegate(this);
|
58
|
+
}, this);
|
59
|
+
if (config.actions) Ext.each(config.actions, function(i){
|
60
|
+
this.addEvents(i.handlerName + 'click');
|
61
|
+
i.handler = this.actionHandler.createDelegate(this);
|
62
|
+
}, this);
|
63
|
+
|
64
|
+
// set events
|
59
65
|
this.on('beforedestroy', function(){
|
60
66
|
// clean-up menus
|
61
67
|
if (this.app && !!this.app.unhostMenus) {
|
62
|
-
// alert('beforedestroy');
|
63
68
|
this.app.unhostMenus(this)
|
64
69
|
}
|
65
70
|
}, this);
|
@@ -68,7 +73,7 @@ Ext.widgetMixIn = {
|
|
68
73
|
},
|
69
74
|
|
70
75
|
feedback:function(msg){
|
71
|
-
if (this.initialConfig.quiet) return false;
|
76
|
+
if (this.initialConfig && this.initialConfig.quiet) return false;
|
72
77
|
if (this.app && !!this.app.showFeedback) {
|
73
78
|
this.app.showFeedback(msg)
|
74
79
|
} else {
|
@@ -103,32 +108,48 @@ Ext.override(Ext.Panel, {
|
|
103
108
|
loadWidget: function(url, params){
|
104
109
|
if (!params) params = {}
|
105
110
|
|
106
|
-
|
111
|
+
// FIXME: a nasty bug: with autoDestroy set to true, it stops working in Ext 2.2.1!
|
112
|
+
this.remove(this.getWidget(), false); // first delete previous widget
|
107
113
|
|
108
114
|
if (!url) return false; // don't load any widget if the url is null
|
109
115
|
|
110
116
|
// we will let the server know which components we have cached
|
111
117
|
var cachedComponentNames = [];
|
112
|
-
for (name in Ext.
|
118
|
+
for (name in Ext.netzke.cache) {
|
113
119
|
cachedComponentNames.push(name);
|
114
120
|
}
|
115
121
|
|
116
122
|
this.disable(); // to visually emphasize loading
|
117
123
|
|
118
|
-
Ext.Ajax.request(
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
124
|
+
Ext.Ajax.request({
|
125
|
+
url:url,
|
126
|
+
params:Ext.apply(params, {components_cache:Ext.encode(cachedComponentNames)}),
|
127
|
+
script:false,
|
128
|
+
callback:function(panel, success, response){
|
129
|
+
var responseObj = Ext.decode(response.responseText);
|
130
|
+
if (responseObj.config) {
|
131
|
+
// we got a normal response
|
132
|
+
|
133
|
+
// evaluate widget's class if it was sent
|
134
|
+
if (responseObj.classDefinition) {
|
135
|
+
eval(responseObj.classDefinition);
|
136
|
+
}
|
137
|
+
|
138
|
+
responseObj.config.parent = this // we might want to know the parent panel in advance (e.g. to know its size)
|
139
|
+
var instance = new Ext.netzke.cache[responseObj.config.widgetClassName](responseObj.config)
|
125
140
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
141
|
+
this.add(instance);
|
142
|
+
this.doLayout();
|
143
|
+
} else {
|
144
|
+
// we didn't get normal response - desplay the flash with eventual errors
|
145
|
+
this.ownerCt.feedback(responseObj.flash)
|
146
|
+
}
|
147
|
+
|
148
|
+
// reenable the panel
|
149
|
+
this.enable();
|
150
|
+
},
|
151
|
+
scope:this
|
152
|
+
})
|
132
153
|
}
|
133
154
|
});
|
134
155
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
class NetzkeLayout < ActiveRecord::Base
|
2
|
-
|
3
|
-
|
2
|
+
EXT_UNRELATED_ATTRIBUTES = %w{ id layout_id position created_at updated_at }
|
4
3
|
# Multi user support
|
5
4
|
def self.user
|
6
5
|
@@user ||= nil
|
@@ -19,7 +18,7 @@ class NetzkeLayout < ActiveRecord::Base
|
|
19
18
|
create(config.merge(:user_id => user_id))
|
20
19
|
end
|
21
20
|
|
22
|
-
def
|
21
|
+
def items
|
23
22
|
items_class.constantize.find_all_by_layout_id(id, :order => 'position')
|
24
23
|
end
|
25
24
|
|
@@ -28,13 +27,21 @@ class NetzkeLayout < ActiveRecord::Base
|
|
28
27
|
end
|
29
28
|
|
30
29
|
def move_item(old_index, new_index)
|
31
|
-
layout_item =
|
30
|
+
layout_item = items[old_index]
|
32
31
|
layout_item.remove_from_list
|
33
32
|
layout_item.insert_at(new_index + 1)
|
34
33
|
end
|
35
34
|
|
36
|
-
def
|
37
|
-
|
35
|
+
def items_arry
|
36
|
+
unrelated_attrs_eraser = EXT_UNRELATED_ATTRIBUTES.inject({}){|h,el| h.merge(el => nil)} # => {:id => nil, :layout_id => nil, ...}
|
37
|
+
items.map(&:attributes).map do |i|
|
38
|
+
# delete unrelated attributes
|
39
|
+
i.merge(unrelated_attrs_eraser).convert_keys {|k| k.to_sym}
|
40
|
+
end
|
38
41
|
end
|
39
42
|
|
43
|
+
def items_arry_without_hidden
|
44
|
+
items_arry.reject{|i| i[:hidden] && !i[:name] == :id} # 'id' is exceptional, should always be sent
|
45
|
+
end
|
46
|
+
|
40
47
|
end
|
data/lib/netzke/base.rb
CHANGED
@@ -8,9 +8,6 @@ module Netzke
|
|
8
8
|
# should be aware of this constant.
|
9
9
|
#
|
10
10
|
class Base
|
11
|
-
# Client-side code (generates JS-class of the widget)
|
12
|
-
include Netzke::JsClassBuilder
|
13
|
-
|
14
11
|
module ClassMethods
|
15
12
|
|
16
13
|
# Global Netzke::Base configuration
|
@@ -83,6 +80,17 @@ module Netzke
|
|
83
80
|
nil
|
84
81
|
end
|
85
82
|
|
83
|
+
# include eventual extra modules
|
84
|
+
def include_extras(file = __FILE__)
|
85
|
+
extras_dir = File.join(File.dirname(file), short_widget_class_name.underscore + "_extras")
|
86
|
+
file_list = Dir.glob("#{extras_dir}/*.rb")
|
87
|
+
for file_name in file_list
|
88
|
+
require file_name
|
89
|
+
module_name = "#{self.name}Extras::#{File.basename(file_name, ".rb").classify}"
|
90
|
+
include module_name.constantize
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
86
94
|
private
|
87
95
|
def set_default_config(default_config)
|
88
96
|
@@config ||= {}
|
@@ -93,6 +101,9 @@ module Netzke
|
|
93
101
|
end
|
94
102
|
extend ClassMethods
|
95
103
|
|
104
|
+
# include extra modules
|
105
|
+
include_extras
|
106
|
+
|
96
107
|
attr_accessor :config, :server_confg, :parent, :logger, :id_name, :permissions
|
97
108
|
attr_reader :pref
|
98
109
|
|
@@ -113,10 +124,19 @@ module Netzke
|
|
113
124
|
Rails.logger
|
114
125
|
end
|
115
126
|
|
127
|
+
def dependency_classes
|
128
|
+
res = []
|
129
|
+
non_late_aggregatees.keys.each do |aggr|
|
130
|
+
res += aggregatee_instance(aggr).dependency_classes
|
131
|
+
end
|
132
|
+
res << short_widget_class_name
|
133
|
+
res.uniq
|
134
|
+
end
|
135
|
+
|
116
136
|
# Store some setting in the database as if it was a hash, e.g.:
|
117
137
|
# persistent_config["window.size"] = 100
|
118
138
|
# persistent_config["window.size"] => 100
|
119
|
-
# This method is
|
139
|
+
# This method is user-aware
|
120
140
|
def persistent_config
|
121
141
|
config_klass = self.class.persistent_config_manager_class
|
122
142
|
config_klass && config_klass.widget_name = id_name # pass to the config class our unique name
|
@@ -178,9 +198,9 @@ module Netzke
|
|
178
198
|
aggregator = self
|
179
199
|
name.to_s.split('__').each do |aggr|
|
180
200
|
aggr = aggr.to_sym
|
181
|
-
|
182
|
-
#
|
183
|
-
widget_class = "Netzke::#{
|
201
|
+
short_class_name = aggregator.aggregatees[aggr][:widget_class_name]
|
202
|
+
raise ArgumentError, "No widget_class_name specified for aggregatee #{aggr} of #{aggregator.config[:name]}" if short_class_name.nil?
|
203
|
+
widget_class = "Netzke::#{short_class_name}".constantize
|
184
204
|
aggregator = widget_class.new(aggregator.aggregatees[aggr].merge(:name => aggr), aggregator)
|
185
205
|
end
|
186
206
|
aggregator
|
@@ -225,28 +245,30 @@ module Netzke
|
|
225
245
|
end
|
226
246
|
end
|
227
247
|
|
228
|
-
|
248
|
+
# method dispatcher - sends method to the proper aggregatee
|
229
249
|
def method_missing(method_name, params = {})
|
230
250
|
widget, *action = method_name.to_s.split('__')
|
231
251
|
widget = widget.to_sym
|
232
252
|
action = !action.empty? && action.join("__").to_sym
|
233
253
|
|
234
|
-
if action
|
235
|
-
|
236
|
-
|
237
|
-
|
254
|
+
if action
|
255
|
+
if aggregatees[widget]
|
256
|
+
# only actions starting with "interface_" are accessible
|
257
|
+
interface_action = action.to_s.index('__') ? action : "interface_#{action}"
|
258
|
+
aggregatee_instance(widget).send(interface_action, params)
|
259
|
+
else
|
260
|
+
aggregatee_missing(widget)
|
261
|
+
end
|
238
262
|
else
|
239
263
|
super
|
240
264
|
end
|
241
265
|
end
|
242
266
|
|
243
|
-
|
244
|
-
def
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
{:config => js_config, :class_definition => js_missing_code(components_cache)}
|
267
|
+
# called when the method_missing tries to processes a non-existing aggregatee
|
268
|
+
def aggregatee_missing(aggr)
|
269
|
+
flash :error => "Unknown aggregatee #{aggr} for widget #{config[:name]}"
|
270
|
+
{:success => false, :flash => @flash}.to_json
|
249
271
|
end
|
250
|
-
|
272
|
+
|
251
273
|
end
|
252
274
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Netzke
|
2
|
+
module BaseExtras
|
3
|
+
module Interface
|
4
|
+
def get_widget(params = {})
|
5
|
+
# if browser does not have our component class cached (and all dependencies), send it to him
|
6
|
+
components_cache = (JSON.parse(params[:components_cache]) if params[:components_cache]) || []
|
7
|
+
{:config => js_config, :class_definition => js_missing_code(components_cache)}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module Netzke
|
2
|
+
module BaseExtras
|
3
|
+
#
|
4
|
+
# Module which provides JS-class generation functionality for the widgets ("client-side"). This code is executed only once per widget class, and the results are cached at the server (unless widget specifies config[:no_caching] => true).
|
5
|
+
# Included into Netzke::Base class
|
6
|
+
# Most of the methods below are meant to be overwritten by a concrete widget class.
|
7
|
+
#
|
8
|
+
module JsBuilder
|
9
|
+
def self.included(base)
|
10
|
+
base.extend ClassMethods
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Instance methods
|
15
|
+
#
|
16
|
+
|
17
|
+
# The config that is sent from the server and is used for instantiating a widget
|
18
|
+
def js_config
|
19
|
+
res = {}
|
20
|
+
|
21
|
+
# recursively include configs of all (non-late) aggregatees, so that the widget can instantiate them
|
22
|
+
aggregatees.each_pair do |aggr_name, aggr_config|
|
23
|
+
next if aggr_config[:late_aggregation]
|
24
|
+
res["#{aggr_name}_config".to_sym] = aggregatee_instance(aggr_name.to_sym).js_config
|
25
|
+
end
|
26
|
+
|
27
|
+
# interface
|
28
|
+
interface = self.class.interface_points.inject({}){|h,interfacep| h.merge(interfacep => widget_action(interfacep))}
|
29
|
+
res.merge!(:interface => interface)
|
30
|
+
|
31
|
+
res.merge!(:widget_class_name => short_widget_class_name)
|
32
|
+
|
33
|
+
res.merge!(js_ext_config)
|
34
|
+
res.merge!(:id => @id_name)
|
35
|
+
|
36
|
+
# include tools and actions
|
37
|
+
res.merge!(:tools => tools) if tools
|
38
|
+
res.merge!(:actions => actions) if actions
|
39
|
+
res.merge!(:bbar => tbar) if tbar
|
40
|
+
res.merge!(:tbar => bbar) if bbar
|
41
|
+
|
42
|
+
# include permissions
|
43
|
+
res.merge!(:permissions => permissions) unless available_permissions.empty?
|
44
|
+
|
45
|
+
res
|
46
|
+
end
|
47
|
+
|
48
|
+
def js_ext_config
|
49
|
+
config[:ext_config] || {}
|
50
|
+
end
|
51
|
+
|
52
|
+
# All the JS-code required by this instance of the widget to be instantiated in the browser.
|
53
|
+
# It includes the JS-class for the widget itself, as well as JS-classes for all widgets' (non-late) aggregatees.
|
54
|
+
def js_missing_code(cached_dependencies = [])
|
55
|
+
dependency_classes.inject("") do |r,k|
|
56
|
+
cached_dependencies.include?(k) ? r : r + "Netzke::#{k}".constantize.js_code(cached_dependencies).strip_js_comments
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def dependency_classes
|
61
|
+
res = []
|
62
|
+
non_late_aggregatees.each_key do |aggr|
|
63
|
+
res << aggregatee_instance(aggr).short_widget_class_name
|
64
|
+
end
|
65
|
+
res.uniq
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# The following methods are used when a widget is generated stand-alone (as a part of a HTML page)
|
70
|
+
#
|
71
|
+
|
72
|
+
# instantiating
|
73
|
+
def js_widget_instance
|
74
|
+
%Q{var #{config[:name].to_js} = new Ext.netzke.cache['#{short_widget_class_name}'](#{js_config.to_js});}
|
75
|
+
end
|
76
|
+
|
77
|
+
# rendering
|
78
|
+
def js_widget_render
|
79
|
+
%Q{#{config[:name].to_js}.render("#{config[:name].to_s.split('_').join('-')}");}
|
80
|
+
end
|
81
|
+
|
82
|
+
# container for rendering
|
83
|
+
def js_widget_html
|
84
|
+
%Q{<div id="#{config[:name].to_s.split('_').join('-')}"></div>}
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
#
|
89
|
+
#
|
90
|
+
|
91
|
+
# widget's actions, tools and toolbars that are loaded at the moment of instantiating a widget
|
92
|
+
def actions; nil; end
|
93
|
+
def tools; nil; end
|
94
|
+
def tbar; nil; end
|
95
|
+
def bbar; nil; end
|
96
|
+
|
97
|
+
# little helpers
|
98
|
+
def this; "this".l; end
|
99
|
+
def null; "null".l; end
|
100
|
+
|
101
|
+
|
102
|
+
# Methods used to create the javascript class (only once per widget class).
|
103
|
+
# The generated code gets cached at the browser, and the widget intstances (at the browser side)
|
104
|
+
# get instantiated from it.
|
105
|
+
# All these methods can be overwritten in case you want to extend the functionality of some pre-built widget
|
106
|
+
# instead of using it as is (using both would cause JS-code duplication)
|
107
|
+
module ClassMethods
|
108
|
+
# the JS (Ext) class that we inherit from on JS-level
|
109
|
+
def js_base_class; "Ext.Panel"; end
|
110
|
+
|
111
|
+
# default config that gets merged with Base#js_config
|
112
|
+
def js_default_config
|
113
|
+
{
|
114
|
+
:title => "config.id.humanize()".l,
|
115
|
+
:listeners => js_listeners,
|
116
|
+
:tools => "config.tools".l,
|
117
|
+
:actions => "config.actions".l,
|
118
|
+
:tbar => "config.tbar".l,
|
119
|
+
:bbar => "config.bbar".l,
|
120
|
+
# :items => "config.items".l,
|
121
|
+
# :items => js_items,
|
122
|
+
:height => 400,
|
123
|
+
:width => 800,
|
124
|
+
:border => false
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
# functions and properties that will be used to extend the functionality of (Ext) JS-class specified in js_base_class
|
129
|
+
def js_extend_properties; {}; end
|
130
|
+
|
131
|
+
# code executed before and after the constructor
|
132
|
+
def js_before_constructor; ""; end
|
133
|
+
def js_after_constructor; ""; end
|
134
|
+
|
135
|
+
# widget's listeners
|
136
|
+
def js_listeners; {}; end
|
137
|
+
|
138
|
+
# widget's menus
|
139
|
+
def js_menus; []; end
|
140
|
+
|
141
|
+
# items
|
142
|
+
def js_items; null; end
|
143
|
+
|
144
|
+
# are we using JS inheritance? for now, if js_base_class is a Netzke class - yes
|
145
|
+
def js_inheritance
|
146
|
+
js_base_class.is_a?(Class)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Declaration of widget's class (stored in the cache storage (Ext.netzke.cache) at the client side
|
150
|
+
# to be reused at the moment of widget instantiation)
|
151
|
+
def js_class
|
152
|
+
if js_inheritance
|
153
|
+
<<-JS
|
154
|
+
Ext.netzke.cache['#{short_widget_class_name}'] = Ext.extend(Ext.netzke.cache.#{js_base_class.short_widget_class_name}, Ext.chainApply([Ext.widgetMixIn, {
|
155
|
+
constructor: function(config){
|
156
|
+
Ext.netzke.cache['#{short_widget_class_name}'].superclass.constructor.call(this, config);
|
157
|
+
}
|
158
|
+
}, #{js_extend_properties.to_js}]))
|
159
|
+
|
160
|
+
JS
|
161
|
+
else
|
162
|
+
js_add_menus = "this.addMenus(#{js_menus.to_js});" unless js_menus.empty?
|
163
|
+
<<-JS
|
164
|
+
Ext.netzke.cache['#{short_widget_class_name}'] = Ext.extend(#{js_base_class}, Ext.chainApply([Ext.widgetMixIn, {
|
165
|
+
constructor: function(config){
|
166
|
+
// comment
|
167
|
+
#{js_before_constructor}
|
168
|
+
Ext.netzke.cache['#{short_widget_class_name}'].superclass.constructor.call(this, Ext.apply(#{js_default_config.to_js}, config));
|
169
|
+
this.widgetInit(config);
|
170
|
+
#{js_after_constructor}
|
171
|
+
#{js_add_menus}
|
172
|
+
}
|
173
|
+
}, #{js_extend_properties.to_js}]))
|
174
|
+
JS
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# all the JS code needed for this class
|
179
|
+
def js_code(cached_dependencies)
|
180
|
+
res = ""
|
181
|
+
|
182
|
+
# include the dependency if doing JS
|
183
|
+
res << js_base_class.js_class if js_inheritance && !cached_dependencies.include?(js_base_class.short_widget_class_name)
|
184
|
+
|
185
|
+
# our own JS class definition
|
186
|
+
res << js_class
|
187
|
+
res
|
188
|
+
end
|
189
|
+
|
190
|
+
def this; "this".l; end
|
191
|
+
def null; "null".l; end
|
192
|
+
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
data/lib/netzke/core_ext.rb
CHANGED
@@ -63,10 +63,14 @@ class String
|
|
63
63
|
self.camelize(:lower)
|
64
64
|
end
|
65
65
|
|
66
|
-
# removes JS-comments (both single-
|
66
|
+
# removes JS-comments (both single- and multi-line) from the string
|
67
67
|
def strip_js_comments
|
68
68
|
regexp = /\/\/.*$|(?m:\/\*.*?\*\/)/
|
69
|
-
self.gsub(regexp, '')
|
69
|
+
self.gsub!(regexp, '')
|
70
|
+
|
71
|
+
# also remove empty lines
|
72
|
+
regexp = /^\s*\n/
|
73
|
+
self.gsub!(regexp, '')
|
70
74
|
end
|
71
75
|
end
|
72
76
|
|
@@ -3,41 +3,41 @@ module Netzke
|
|
3
3
|
# An invisible component that provides feedback service for all Netzke widgets
|
4
4
|
#
|
5
5
|
class FeedbackGhost < Base
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
6
|
+
def self.js_base_class
|
7
|
+
"Ext.Component" # yes, invisible
|
8
|
+
end
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
10
|
+
def self.js_extend_properties
|
11
|
+
{
|
12
|
+
:show_feedback => <<-JS.l,
|
13
|
+
function(msg){
|
14
|
+
var createBox = function(s, l){
|
15
|
+
return ['<div class="msg">',
|
16
|
+
'<div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>',
|
17
|
+
'<div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc">', s, '</div></div></div>',
|
18
|
+
'<div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>',
|
19
|
+
'</div>'].join('');
|
20
|
+
}
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
var showBox = function(msg, lvl){
|
23
|
+
if (!lvl) lvl = 'notice';
|
24
|
+
var msgCt = Ext.DomHelper.insertFirst(document.body, {'class':'netzke-feedback'}, true);
|
25
|
+
var m = Ext.DomHelper.append(msgCt, {html:createBox(msg,lvl)}, true);
|
26
|
+
m.slideIn('t').pause(2).ghost("b", {remove:true});
|
27
|
+
}
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
}
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
29
|
+
if (typeof msg != 'string') {
|
30
|
+
var compoundMsg = "";
|
31
|
+
Ext.each(msg, function(m){
|
32
|
+
compoundMsg += m.msg + '<br>';
|
33
|
+
})
|
34
|
+
if (compoundMsg != "") showBox(compoundMsg, null) // the second parameter will be level
|
35
|
+
} else {
|
36
|
+
showBox(msg)
|
37
|
+
}
|
38
|
+
}
|
39
|
+
JS
|
40
|
+
}
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
data/lib/netzke-core.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# NetzkeCore
|
2
|
-
require 'netzke/js_class_builder'
|
3
2
|
require 'netzke/base'
|
4
|
-
require 'netzke/feedback_ghost'
|
5
3
|
require 'netzke/action_view_ext'
|
6
4
|
require 'netzke/controller_extensions'
|
7
5
|
require 'netzke/core_ext'
|
8
6
|
require 'netzke/routing'
|
9
7
|
|
8
|
+
require 'netzke/feedback_ghost'
|
9
|
+
|
10
10
|
# Vendor
|
11
11
|
require 'vendor/facets/hash/recursive_merge'
|
12
12
|
|
data/netzke-core.gemspec
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{netzke-core}
|
5
|
-
s.version = "0.2.
|
5
|
+
s.version = "0.2.6"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Sergei Kozlov"]
|
9
|
-
s.date = %q{2009-
|
9
|
+
s.date = %q{2009-02-12}
|
10
10
|
s.description = %q{Build ExtJS/Rails widgets with minimum effort}
|
11
11
|
s.email = %q{sergei@writelesscode.com}
|
12
|
-
s.extra_rdoc_files = ["CHANGELOG", "lib/app/controllers/netzke_controller.rb", "lib/app/models/netzke_layout.rb", "lib/app/models/netzke_preference.rb", "lib/netzke/action_view_ext.rb", "lib/netzke/base.rb", "lib/netzke/
|
13
|
-
s.files = ["CHANGELOG", "css/core.css", "generators/netzke_core/netzke_core_generator.rb", "generators/netzke_core/templates/create_netzke_layouts.rb", "generators/netzke_core/templates/create_netzke_preferences.rb", "generators/netzke_core/USAGE", "init.rb", "install.rb", "javascripts/core.js", "lib/app/controllers/netzke_controller.rb", "lib/app/models/netzke_layout.rb", "lib/app/models/netzke_preference.rb", "lib/netzke/action_view_ext.rb", "lib/netzke/base.rb", "lib/netzke/
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "lib/app/controllers/netzke_controller.rb", "lib/app/models/netzke_layout.rb", "lib/app/models/netzke_preference.rb", "lib/netzke/action_view_ext.rb", "lib/netzke/base.rb", "lib/netzke/base_extras/interface.rb", "lib/netzke/base_extras/js_builder.rb", "lib/netzke/controller_extensions.rb", "lib/netzke/core_ext.rb", "lib/netzke/feedback_ghost.rb", "lib/netzke/routing.rb", "lib/netzke-core.rb", "lib/vendor/facets/hash/recursive_merge.rb", "LICENSE", "README.mdown", "tasks/netzke_core_tasks.rake"]
|
13
|
+
s.files = ["CHANGELOG", "css/core.css", "generators/netzke_core/netzke_core_generator.rb", "generators/netzke_core/templates/create_netzke_layouts.rb", "generators/netzke_core/templates/create_netzke_preferences.rb", "generators/netzke_core/USAGE", "init.rb", "install.rb", "javascripts/core.js", "lib/app/controllers/netzke_controller.rb", "lib/app/models/netzke_layout.rb", "lib/app/models/netzke_preference.rb", "lib/netzke/action_view_ext.rb", "lib/netzke/base.rb", "lib/netzke/base_extras/interface.rb", "lib/netzke/base_extras/js_builder.rb", "lib/netzke/controller_extensions.rb", "lib/netzke/core_ext.rb", "lib/netzke/feedback_ghost.rb", "lib/netzke/routing.rb", "lib/netzke-core.rb", "lib/vendor/facets/hash/recursive_merge.rb", "LICENSE", "Manifest", "netzke-core.gemspec", "Rakefile", "README.mdown", "tasks/netzke_core_tasks.rake", "test/app_root/app/controllers/application.rb", "test/app_root/config/boot.rb", "test/app_root/config/database.yml", "test/app_root/config/environment.rb", "test/app_root/config/environments/in_memory.rb", "test/app_root/config/environments/mysql.rb", "test/app_root/config/environments/postgresql.rb", "test/app_root/config/environments/sqlite.rb", "test/app_root/config/environments/sqlite3.rb", "test/app_root/config/routes.rb", "test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb", "test/app_root/script/console", "test/core_ext_test.rb", "test/netzke_core_test.rb", "test/netzke_preference_test.rb", "test/test_helper.rb", "uninstall.rb"]
|
14
14
|
s.has_rdoc = true
|
15
15
|
s.homepage = %q{http://writelesscode.com}
|
16
16
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Netzke-core", "--main", "README.mdown"]
|
data/test/netzke_core_test.rb
CHANGED
@@ -38,6 +38,12 @@ module Netzke
|
|
38
38
|
|
39
39
|
class DeepNestedWidget < Base
|
40
40
|
end
|
41
|
+
|
42
|
+
class JsInheritanceWidget < Base
|
43
|
+
def self.js_base_class
|
44
|
+
Widget
|
45
|
+
end
|
46
|
+
end
|
41
47
|
end
|
42
48
|
|
43
49
|
# mocking the User class
|
@@ -129,12 +135,19 @@ class NetzkeCoreTest < ActiveSupport::TestCase
|
|
129
135
|
end
|
130
136
|
|
131
137
|
# test "dependencies in JS class generators" do
|
132
|
-
#
|
133
|
-
# assert(
|
134
|
-
# assert(
|
135
|
-
# assert(
|
138
|
+
# widget = Widget.new
|
139
|
+
# assert(widget.js_missing_code.index("Ext.netzke.cache['NestedWidgetOne']"))
|
140
|
+
# assert(widget.js_missing_code.index("Ext.netzke.cache['NestedWidgetTwo']"))
|
141
|
+
# assert(widget.js_missing_code.index("Ext.netzke.cache['DeepNestedWidget']"))
|
142
|
+
# assert(widget.js_missing_code.index("Ext.netzke.cache['Widget']"))
|
136
143
|
# end
|
137
144
|
|
145
|
+
test "dependency classes" do
|
146
|
+
widget = Widget.new
|
147
|
+
# not testing the order
|
148
|
+
assert(%w{DeepNestedWidget NestedWidgetOne NestedWidgetTwo Widget}.inject(true){|r, k| r && widget.dependency_classes.include?(k)})
|
149
|
+
end
|
150
|
+
|
138
151
|
test "widget instance by config" do
|
139
152
|
widget = Netzke::Base.instance_by_config({:widget_class_name => 'Widget', :name => 'a_widget'})
|
140
153
|
assert(Widget, widget.class)
|
@@ -145,6 +158,11 @@ class NetzkeCoreTest < ActiveSupport::TestCase
|
|
145
158
|
assert_equal(NetzkeLayout, Netzke::Base.layout_manager_class)
|
146
159
|
end
|
147
160
|
|
161
|
+
test "js inheritance" do
|
162
|
+
widget = JsInheritanceWidget.new
|
163
|
+
assert(widget.js_missing_code.index("Ext.netzke.cache['JsInheritanceWidget']"))
|
164
|
+
assert(widget.js_missing_code.index("Ext.netzke.cache['Widget']"))
|
165
|
+
end
|
148
166
|
# test "multiuser" do
|
149
167
|
# Netzke::Base.current_user = User.new(1)
|
150
168
|
# Widget.new(:prohibit => :all, :name => 'widget')
|
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.2.
|
4
|
+
version: 0.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergei Kozlov
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-02-12 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -26,10 +26,11 @@ extra_rdoc_files:
|
|
26
26
|
- lib/app/models/netzke_preference.rb
|
27
27
|
- lib/netzke/action_view_ext.rb
|
28
28
|
- lib/netzke/base.rb
|
29
|
+
- lib/netzke/base_extras/interface.rb
|
30
|
+
- lib/netzke/base_extras/js_builder.rb
|
29
31
|
- lib/netzke/controller_extensions.rb
|
30
32
|
- lib/netzke/core_ext.rb
|
31
33
|
- lib/netzke/feedback_ghost.rb
|
32
|
-
- lib/netzke/js_class_builder.rb
|
33
34
|
- lib/netzke/routing.rb
|
34
35
|
- lib/netzke-core.rb
|
35
36
|
- lib/vendor/facets/hash/recursive_merge.rb
|
@@ -51,10 +52,11 @@ files:
|
|
51
52
|
- lib/app/models/netzke_preference.rb
|
52
53
|
- lib/netzke/action_view_ext.rb
|
53
54
|
- lib/netzke/base.rb
|
55
|
+
- lib/netzke/base_extras/interface.rb
|
56
|
+
- lib/netzke/base_extras/js_builder.rb
|
54
57
|
- lib/netzke/controller_extensions.rb
|
55
58
|
- lib/netzke/core_ext.rb
|
56
59
|
- lib/netzke/feedback_ghost.rb
|
57
|
-
- lib/netzke/js_class_builder.rb
|
58
60
|
- lib/netzke/routing.rb
|
59
61
|
- lib/netzke-core.rb
|
60
62
|
- lib/vendor/facets/hash/recursive_merge.rb
|
@@ -1,174 +0,0 @@
|
|
1
|
-
module Netzke
|
2
|
-
#
|
3
|
-
# Module which provides JS-class generation functionality for the widgets ("client-side"). This code is executed only once per widget class, and the results are cached at the server (unless widget specifies config[:no_caching] => true).
|
4
|
-
# Included into Netzke::Base class
|
5
|
-
# Most of the methods below are meant to be overwritten by a concrete widget class.
|
6
|
-
#
|
7
|
-
module JsClassBuilder
|
8
|
-
def self.included(base)
|
9
|
-
base.extend ClassMethods
|
10
|
-
end
|
11
|
-
|
12
|
-
#
|
13
|
-
# Instance methods
|
14
|
-
#
|
15
|
-
|
16
|
-
# The config that is sent from the server and is used for instantiating a widget
|
17
|
-
def js_config
|
18
|
-
res = {}
|
19
|
-
|
20
|
-
# recursively include configs of all (non-late) aggregatees, so that the widget can instantiate them
|
21
|
-
aggregatees.each_pair do |aggr_name, aggr_config|
|
22
|
-
next if aggr_config[:late_aggregation]
|
23
|
-
res["#{aggr_name}_config".to_sym] = aggregatee_instance(aggr_name.to_sym).js_config
|
24
|
-
end
|
25
|
-
|
26
|
-
# interface
|
27
|
-
interface = self.class.interface_points.inject({}){|h,interfacep| h.merge(interfacep => widget_action(interfacep))}
|
28
|
-
res.merge!(:interface => interface)
|
29
|
-
|
30
|
-
res.merge!(:widget_class_name => short_widget_class_name)
|
31
|
-
|
32
|
-
res.merge!(js_ext_config)
|
33
|
-
res.merge!(:id => @id_name)
|
34
|
-
|
35
|
-
# include tools and actions
|
36
|
-
res.merge!(:tools => tools) if tools
|
37
|
-
res.merge!(:actions => actions) if actions
|
38
|
-
res.merge!(:bbar => tbar) if tbar
|
39
|
-
res.merge!(:tbar => bbar) if bbar
|
40
|
-
|
41
|
-
# include permissions
|
42
|
-
res.merge!(:permissions => permissions) unless available_permissions.empty?
|
43
|
-
|
44
|
-
res
|
45
|
-
end
|
46
|
-
|
47
|
-
def js_ext_config
|
48
|
-
config[:ext_config] || {}
|
49
|
-
end
|
50
|
-
|
51
|
-
#
|
52
|
-
# The following methods are used when a widget is generated stand-alone (as a part of a HTML page)
|
53
|
-
#
|
54
|
-
|
55
|
-
# instantiating
|
56
|
-
def js_widget_instance
|
57
|
-
%Q{var #{config[:name].to_js} = new Ext.componentCache['#{short_widget_class_name}'](#{js_config.to_js});}
|
58
|
-
end
|
59
|
-
|
60
|
-
# rendering
|
61
|
-
def js_widget_render
|
62
|
-
%Q{#{config[:name].to_js}.render("#{config[:name].to_s.split('_').join('-')}");}
|
63
|
-
end
|
64
|
-
|
65
|
-
# container for rendering
|
66
|
-
def js_widget_html
|
67
|
-
%Q{<div id="#{config[:name].to_s.split('_').join('-')}"></div>}
|
68
|
-
end
|
69
|
-
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
|
74
|
-
# All the JS-code required by this *instance* of the widget. It includes the JS-class for the widget
|
75
|
-
# itself, as well as JS-classes for all widgets (non-late) aggregatees.
|
76
|
-
def js_missing_code(cached_dependencies = [])
|
77
|
-
result = ""
|
78
|
-
dependencies.each do |dep_name|
|
79
|
-
unless cached_dependencies.include?(dep_name)
|
80
|
-
dependency_class = "Netzke::#{dep_name}".constantize
|
81
|
-
result << dependency_class.js_class
|
82
|
-
end
|
83
|
-
end
|
84
|
-
result
|
85
|
-
end
|
86
|
-
|
87
|
-
# widget's actions, tools and toolbars that are loaded at the moment of instantiating a widget
|
88
|
-
def actions; nil; end
|
89
|
-
def tools; nil; end
|
90
|
-
def tbar; nil; end
|
91
|
-
def bbar; nil; end
|
92
|
-
|
93
|
-
# little helpers
|
94
|
-
def this; "this".l; end
|
95
|
-
def null; "null".l; end
|
96
|
-
|
97
|
-
|
98
|
-
# Methods used to create the javascript class (only once per widget class).
|
99
|
-
# The generated code gets cached at the browser, and the widget intstances (at the browser side)
|
100
|
-
# get instantiated from it.
|
101
|
-
# All these methods can be overwritten in case you want to extend the functionality of some pre-built widget
|
102
|
-
# instead of using it as is (using both would cause JS-code duplication)
|
103
|
-
module ClassMethods
|
104
|
-
# the JS (Ext) class that we inherit from on JS-level
|
105
|
-
def js_base_class; "Ext.Panel"; end
|
106
|
-
|
107
|
-
# functions and properties that will be used to extend the functionality of (Ext) JS-class specified in js_base_class
|
108
|
-
def js_extend_properties; {}; end
|
109
|
-
|
110
|
-
# code executed before and after the constructor
|
111
|
-
def js_before_constructor; ""; end
|
112
|
-
def js_after_constructor; ""; end
|
113
|
-
|
114
|
-
# widget's listeners
|
115
|
-
def js_listeners; {}; end
|
116
|
-
|
117
|
-
# widget's menus
|
118
|
-
def js_menus; []; end
|
119
|
-
|
120
|
-
# items
|
121
|
-
def js_items; null; end
|
122
|
-
|
123
|
-
# default config that is always passed into the constructor
|
124
|
-
def js_default_config
|
125
|
-
{
|
126
|
-
:title => "config.id.humanize()".l,
|
127
|
-
:listeners => js_listeners,
|
128
|
-
:tools => "config.tools".l,
|
129
|
-
:actions => "config.actions".l,
|
130
|
-
:tbar => "config.tbar".l,
|
131
|
-
:bbar => "config.bbar".l,
|
132
|
-
# :items => "config.items".l,
|
133
|
-
# :items => js_items,
|
134
|
-
:height => 400,
|
135
|
-
:width => 800,
|
136
|
-
:border => false
|
137
|
-
}
|
138
|
-
end
|
139
|
-
|
140
|
-
# Declaration of widget's class (stored in the cache storage (Ext.componentCache) at the client side
|
141
|
-
# to be reused at the moment of widget instantiation)
|
142
|
-
def js_class
|
143
|
-
<<-JS
|
144
|
-
Ext.componentCache['#{short_widget_class_name}'] = Ext.extend(#{js_base_class}, Ext.chainApply([Ext.widgetMixIn, {
|
145
|
-
constructor: function(config){
|
146
|
-
this.widgetInit(config);
|
147
|
-
#{js_before_constructor}
|
148
|
-
Ext.componentCache['#{short_widget_class_name}'].superclass.constructor.call(this, Ext.apply(#{js_default_config.to_js}, config));
|
149
|
-
#{js_after_constructor}
|
150
|
-
this.setEvents();
|
151
|
-
this.addMenus(#{js_menus.to_js});
|
152
|
-
}
|
153
|
-
}, #{js_extend_properties.to_js}]))
|
154
|
-
JS
|
155
|
-
end
|
156
|
-
|
157
|
-
# class definition of the widget plus that of all the dependencies, minus those that are specified as cached_dependencies
|
158
|
-
def js_missing_code(cached_dependencies = [])
|
159
|
-
result = ""
|
160
|
-
dependencies.each do |dep_name|
|
161
|
-
dependency_class = "Netzke::#{dep_name}".constantize
|
162
|
-
result << dependency_class.js_class_code(cached_dependencies)
|
163
|
-
end
|
164
|
-
result << js_class.strip_js_comments unless cached_dependencies.include?(short_widget_class_name) && !config[:no_caching]
|
165
|
-
result
|
166
|
-
end
|
167
|
-
|
168
|
-
def this; "this".l; end
|
169
|
-
def null; "null".l; end
|
170
|
-
|
171
|
-
end
|
172
|
-
|
173
|
-
end
|
174
|
-
end
|