netzke-core 0.2.4 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|