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 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