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 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
- See the tutorials on http://blog.writelesscode.com
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
- widgetInit:function(config){
52
- this.app = Ext.getCmp('feedback_ghost');
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
- setEvents: function(){
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
- this.remove(this.getWidget()); // first delete previous widget
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.componentCache) {
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
- {url:url, params:Ext.apply(params, {components_cache:Ext.encode(cachedComponentNames)}), script:false, callback:function(panel, success, response){
120
- var response = Ext.decode(response.responseText);
121
- if (response['classDefinition']) eval(response['classDefinition']); // evaluate widget's class if it was sent
122
-
123
- response.config.parent = this // we might want to know the parent panel in advance (e.g. to know its size)
124
- var instance = new Ext.componentCache[response.config.widgetClassName](response.config)
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
- this.add(instance);
127
- this.doLayout();
128
- this.enable();
129
- }, scope:this}
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
- UNRELATED_ATTRS = %w(created_at updated_at position layout_id)
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 layout_items
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 = layout_items[old_index]
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 items_hash
37
- layout_items.map(&:attributes).map{|item| item.delete_if{|k,v| UNRELATED_ATTRS.include?(k)}}.map{ |i| i.convert_keys{ |k| k.to_sym } }
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 current_user-aware
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
- # TODO: should we put all the classes under Netzke::-scope?
182
- # widget_class = full_widget_class_name(aggregator.aggregatees[aggr][:widget_class_name]).constantize
183
- widget_class = "Netzke::#{aggregator.aggregatees[aggr][:widget_class_name]}".constantize
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
- ## method dispatcher - sends method to the proper aggregatee
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 && aggregatees[widget]
235
- # only actions starting with "interface_" are accessible
236
- interface_action = action.to_s.index('__') ? action : "interface_#{action}"
237
- aggregatee_instance(widget).send(interface_action, params)
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
- #### Interface
244
- def get_widget(params = {})
245
- # if browser does not have our component class cached (and all dependencies), send it to him
246
- components_cache = (JSON.parse(params[:components_cache]) if params[:components_cache]) || []
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
@@ -63,10 +63,14 @@ class String
63
63
  self.camelize(:lower)
64
64
  end
65
65
 
66
- # removes JS-comments (both single-line and multiple-line) from the string
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
- class << self
7
- def js_base_class
8
- "Ext.Component" # yes, invisible
9
- end
6
+ def self.js_base_class
7
+ "Ext.Component" # yes, invisible
8
+ end
10
9
 
11
- def js_extend_properties
12
- {
13
- :show_feedback => <<-JS.l,
14
- function(msg){
15
- var createBox = function(s, l){
16
- return ['<div class="msg">',
17
- '<div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>',
18
- '<div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc">', s, '</div></div></div>',
19
- '<div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>',
20
- '</div>'].join('');
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
- var showBox = function(msg, lvl){
24
- if (!lvl) lvl = 'notice';
25
- var msgCt = Ext.DomHelper.insertFirst(document.body, {'class':'netzke-feedback'}, true);
26
- var m = Ext.DomHelper.append(msgCt, {html:createBox(msg,lvl)}, true);
27
- m.slideIn('t').pause(2).ghost("b", {remove:true});
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
- if (typeof msg != 'string') {
31
- Ext.each(msg, function(m){
32
- showBox(m.msg, m.level)
33
- })
34
- } else {
35
- showBox(msg)
36
- }
37
- }
38
- JS
39
- }
40
- end
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.4"
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-01-28}
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/controller_extensions.rb", "lib/netzke/core_ext.rb", "lib/netzke/feedback_ghost.rb", "lib/netzke/js_class_builder.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/controller_extensions.rb", "lib/netzke/core_ext.rb", "lib/netzke/feedback_ghost.rb", "lib/netzke/js_class_builder.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"]
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"]
@@ -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
- # js_code = Widget.js_class
133
- # assert(js_code.index("Ext.componentCache['NestedWidgetOne']"))
134
- # assert(js_code.index("Ext.componentCache['NestedWidgetTwo']"))
135
- # assert(js_code.index("Ext.componentCache['DeepNestedWidget']"))
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
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-01-28 00:00:00 -06:00
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