netzke-core 0.4.5.2 → 0.5.0

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.rdoc CHANGED
@@ -1,3 +1,15 @@
1
+ = v0.5.0
2
+ * Compatibility with Ext 3.1.0
3
+ * API change: Netzke widget's now should be declared directly in the views instead of controllers.
4
+ * API change: all ExtJS and Netzke JavaScript and styles are now loaded with the help of <tt>netzke_init</tt> helper.
5
+ * API change: <tt>persistence_key</tt> option replaces <tt>persistent_config_id</tt> option.
6
+ * Impr: headers in panels in the "config" mode now show the widget's global ID.
7
+ * New: required ExtJS version check introduced at initial Netzke load.
8
+ * Depr: :widget_class_name option is deprecated, use :class_name.
9
+ * DRY: now there's no need to always define "actions" method, use it to override the defaults, which are automatically calculated based on configuration for toolbars/menu.
10
+ * Impr: each generated JS class now has its unique xtype, e.g. "netzkegridpanel".
11
+ * Fix: FeedbackGhost moved over from netzke-basepack.
12
+
1
13
  = v0.4.5.2 - 2009-11-09
2
14
  * Fix: Hash#convert_keys and Array#convert_keys in core extensions are now renamed into deep_convert_keys, and now always plainly do what they're expected to do: recursively convert keys according to given block.
3
15
 
data/README.rdoc CHANGED
@@ -1,12 +1,16 @@
1
1
  = netzke-core
2
2
  Create Ext JS + Rails reusable components (widgets) with minimum effort.
3
3
 
4
- This is the bare bones of the Netzke framework. Use it to build your own widgets from scratch. For pre-built widgets (like panels, grids, forms, trees, applications), see the netzke-basepack project.
4
+ This is the bare bones of the Netzke framework. Use it to build your own widgets from scratch. For pre-built widgets (like panels, grids, forms, trees, applications), see the netzke-basepack (http://github.com/skozlov/netzke-basepack) project.
5
5
 
6
6
  The idea behind the Netzke framework is that it allows you write reusable client/server code. Create a widget, and then embed it (or load it dynamically) into your Ext-based applications or HTML pages. For more info, see the links below.
7
7
 
8
8
  == Instalation
9
- The gem is hosted on gemcutter. If you haven't yet enabled gemcutter, run the following:
9
+ For the latest ("edge") stuff, install as plugin (recommended!):
10
+
11
+ ./script/plugin install git://github.com/skozlov/netzke-core.git
12
+
13
+ Otherwise, install as gem. The gem is hosted on gemcutter. If you haven't yet enabled gemcutter, run the following:
10
14
 
11
15
  sudo gem install gemcutter && gem tumble
12
16
 
@@ -14,17 +18,38 @@ Install the gem
14
18
 
15
19
  sudo gem install netzke-core
16
20
 
17
- If you want to experiment with the most recent changes, install this as a plugin:
21
+ Generate the migrations for Netzke persistent storage:
18
22
 
19
- script/plugin install git://github.com/skozlov/netzke-core.git
23
+ ./script/generate netzke_core
20
24
 
25
+ Run the migrations.
26
+
27
+ Netzke assumes that your ExtJS library is in public/extjs (which may be a symbolic link).
28
+
21
29
  == Usage
22
- Introduction to Netzke framework: http://github.com/skozlov/netzke/tree/master
30
+ Here's how to embed a Netzke widget into your Rails view.
31
+
32
+ 1. In your layout, within the "head" tag, use the <tt>netzke_init</tt> helper to include all the necessary JavaScript and styles.
33
+
34
+ <%= netzke_init %>
35
+
36
+ 2. In your view use the <tt>netzke</tt> helper wherever you want to insert a widget.
37
+
38
+ <%= netzke :grid_panel, :data_class_name => "User", :columns => [:id, :name, :created_at] %>
39
+
40
+ (here we use the GridPanel widget from the netzke-basepack project)
41
+
42
+ == More info
43
+ Introduction to Netzke framework and wiki: http://github.com/skozlov/netzke
44
+
45
+ Twitter: <http://twitter.com/skozlov>
46
+
47
+ Tutorials: <http://blog.writelesscode.com>
23
48
 
24
- Tutorials: http://blog.writelesscode.com
49
+ Live-demo: <http://netzke-demo.writelesscode.com>
25
50
 
26
- Live-demo: http://netzke-demo.writelesscode.com
51
+ The netzke-basepack project (pre-built full-featured widgets): <http://github.com/skozlov/netzke-basepack>
27
52
 
28
- The netzke-basepack project (pre-built full-featured widgets): http://github.com/skozlov/netzke-basepack/tree/master
53
+ ---
29
54
 
30
55
  Copyright (c) 2009 Sergei Kozlov, released under the MIT license
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  begin
2
2
  require 'jeweler'
3
3
  Jeweler::Tasks.new do |gemspec|
4
- gemspec.version = "0.4.5.2"
4
+ gemspec.version = "0.5.0"
5
5
  gemspec.name = "netzke-core"
6
6
  gemspec.summary = "Build ExtJS/Rails widgets with minimum effort"
7
7
  gemspec.description = "Allows building ExtJS/Rails reusable code in a DRY way"
data/javascripts/core.js CHANGED
@@ -2,18 +2,20 @@
2
2
  This file gets loaded along with the rest of Ext library at the initial load
3
3
  */
4
4
 
5
+ // Check Ext JS version
6
+ var requiredExtVersion = "3.1.0";
7
+ var currentExtVersion = Ext.version;
8
+ if (requiredExtVersion !== currentExtVersion) {
9
+ alert("Netzke needs Ext " + requiredExtVersion + ". You have " + currentExtVersion + ".");
10
+ }
11
+
12
+ // Initial stuff
5
13
  Ext.BLANK_IMAGE_URL = "/extjs/resources/images/default/s.gif";
6
14
  Ext.ns('Ext.netzke'); // namespace for extensions that depend on Ext
7
15
  Ext.ns('Netzke'); // Netzke namespace
8
- Netzke.cache = {}; // empty Netzke cache at the moment of loading
9
16
 
10
17
  Ext.QuickTips.init();
11
18
 
12
- // To comply with Rails' forgery protection
13
- Ext.Ajax.extraParams = {
14
- authenticity_token : Ext.authenticityToken
15
- };
16
-
17
19
  // Type detection functions
18
20
  Netzke.isObject = function(o) {
19
21
  return (o != null && typeof o == "object" && o.constructor.toString() == Object.toString());
@@ -100,10 +102,33 @@ Ext.widgetMixIn = {
100
102
 
101
103
  // build the cached widgets list to send it to the server
102
104
  var cachedWidgetNames = "";
103
- for (name in Netzke.cache) {
104
- cachedWidgetNames += name + ",";
105
- }
106
- apiParams.cache = cachedWidgetNames;
105
+
106
+ // recursive function that checks the properties of the caller ("this") and returns the list of those that look like constructor, i.e. have an "xtype" property themselves
107
+ var classesList = function(pref){
108
+ var res = [];
109
+ for (name in this) {
110
+ if (this[name].xtype) {
111
+ res.push(pref + name);
112
+ this[name].classesList = classesList; // define the same function on each property on the fly
113
+ res = res.concat(this[name].classesList(pref + name + ".")); // ... and call it, providing our name along with the scope
114
+ }
115
+ }
116
+ return res;
117
+ };
118
+
119
+ // assign this function to Netzke.classes and call it
120
+ Netzke.classes.classesList = classesList;
121
+ var cl = Netzke.classes.classesList("");
122
+
123
+ // join the classes into a coma-separated list
124
+ var cache = "";
125
+ Ext.each(cl, function(c){cache += c + ",";});
126
+
127
+ // for (name in Netzke.classes) {
128
+ // cachedWidgetNames += name + ",";
129
+ // }
130
+ // apiParams.cache = cachedWidgetNames;
131
+ apiParams.cache = cache;
107
132
 
108
133
  // remember the passed callback for the future
109
134
  if (params.callback) {
@@ -291,13 +316,18 @@ Ext.widgetMixIn = {
291
316
  }
292
317
  },
293
318
 
319
+ // Returns API url based on provided API point
320
+ buildApiUrl: function(apip){
321
+ return "/netzke/" + this.id + "__" + apip;
322
+ },
323
+
294
324
  // Does the call to the server and processes the response
295
325
  callServer : function(intp, params, callback, scope){
296
326
  if (!params) params = {};
297
- Ext.Ajax.request({
298
- params : params,
299
- url : this.id + "__" + intp,
300
- callback : function(options, success, response){
327
+ Ext.Ajax.request({
328
+ params: params,
329
+ url: this.buildApiUrl(intp),
330
+ callback: function(options, success, response){
301
331
  if (success) {
302
332
  // execute commands from server
303
333
  this.bulkExecute(Ext.decode(response.responseText));
@@ -404,11 +434,15 @@ Ext.widgetMixIn = {
404
434
  }
405
435
 
406
436
  // Set title
407
- if (!config.title) {
408
- config.title = config.id.humanize();
437
+ if (config.mode === "config"){
438
+ if (!config.title) {
439
+ config.title = '[' + config.id + ']';
440
+ } else {
441
+ config.title = config.title + ' [' + config.id + ']';
442
+ }
409
443
  } else {
410
- if (config.mode === "config") {
411
- config.title = config.title + ' (' + config.id + ')';
444
+ if (!config.title) {
445
+ config.title = config.id.humanize();
412
446
  }
413
447
  }
414
448
  },
@@ -525,8 +559,6 @@ Ext.override(Ext.Container, {
525
559
  Ext.each(n.split("."), function(s){
526
560
  klass = klass[s];
527
561
  });
528
- // Caching the class
529
- Netzke.cache[n] = true;
530
562
  return klass;
531
563
  },
532
564
 
@@ -542,4 +574,36 @@ Ext.override(Ext.Container, {
542
574
  this.doLayout();
543
575
  }
544
576
  }
545
- });
577
+ });
578
+
579
+
580
+ // Feedback Ghost
581
+ Netzke.FeedbackGhost = function(){};
582
+ Ext.apply(Netzke.FeedbackGhost.prototype, {
583
+ showFeedback: function(msg){
584
+ var createBox = function(s, l){
585
+ return ['<div class="msg">',
586
+ '<div class="x-box-tl"><div class="x-box-tr"><div class="x-box-tc"></div></div></div>',
587
+ '<div class="x-box-ml"><div class="x-box-mr"><div class="x-box-mc">', s, '</div></div></div>',
588
+ '<div class="x-box-bl"><div class="x-box-br"><div class="x-box-bc"></div></div></div>',
589
+ '</div>'].join('');
590
+ }
591
+
592
+ var showBox = function(msg, lvl){
593
+ if (!lvl) {lvl = 'notice'};
594
+ var msgCt = Ext.DomHelper.insertFirst(document.body, {'class':'netzke-feedback'}, true);
595
+ var m = Ext.DomHelper.append(msgCt, {html:createBox(msg,lvl)}, true);
596
+ m.slideIn('t').pause(2).ghost("b", {remove:true});
597
+ }
598
+
599
+ if (typeof msg != 'string') {
600
+ var compoundMsg = "";
601
+ Ext.each(msg, function(m){
602
+ compoundMsg += m.msg + '<br>';
603
+ });
604
+ if (compoundMsg != "") showBox(compoundMsg, null); // the second parameter will be level
605
+ } else {
606
+ showBox(msg);
607
+ }
608
+ }
609
+ });
@@ -1,5 +1,4 @@
1
- class NetzkeController < ActionController::Base
2
-
1
+ class NetzkeController < ApplicationController
3
2
  def index
4
3
  redirect_to :action => :test_widgets
5
4
  end
@@ -27,68 +26,19 @@ class NetzkeController < ActionController::Base
27
26
  end
28
27
  end
29
28
 
30
- #
31
- # Primitive tests to quickly test the widgets (TODO: move to the basepack)
32
- #
33
-
34
- # FormPanel
35
- netzke :form_panel, :persistent_config => false, :label_align => "top", :columns => [
36
- {:name => 'field_one', :xtype => 'textarea'},
37
- {:name => 'field_two', :xtype => 'textarea'}
38
- ]
39
-
40
- # BorderLayoutPanel
41
- netzke :border_layout_panel, :regions => {
42
- :west => {
43
- :widget_class_name => "Panel",
44
- :region_config => {:width => 300, :split => true}
45
- },
46
- :center => {
47
- :widget_class_name => "Panel"
48
- }
49
- }
50
-
51
- # TabPanel
52
- netzke :tab_panel, :items => [{
53
- :widget_class_name => "Panel",
54
- :ext_config => {
55
- :html => "Panel 1",
56
- },
57
- # :active => true
58
- },{
59
- :widget_class_name => "Panel",
60
- :ext_config => {
61
- :html => "Panel 2",
62
- }
63
- }]
64
-
65
- # AccordionPanel
66
- netzke :accordion_panel, :items => [{
67
- :widget_class_name => "Panel",
68
- :ext_config => {
69
- :html => "Panel 1",
70
- },
71
- :active => true
72
- },{
73
- :widget_class_name => "Panel",
74
- :ext_config => {
75
- :html => "Panel 2",
76
- }
77
- }]
29
+ def method_missing(method_name)
30
+ widget_name, *action = method_name.to_s.split('__')
31
+ widget_name = widget_name.to_sym
32
+ action = !action.empty? && action.join("__").to_sym
78
33
 
79
- # BasicApp
80
- netzke :basic_app
81
-
82
- netzke :panel, :ext_config => {:html => "Panel", :title => "Test panel", :border => true}
83
-
84
- def test_widgets
85
- html = "<h3>Quick primitive widgets tests</h3>"
86
-
87
- self.class.widget_config_storage.each_key.map(&:to_s).sort.each do |w|
88
- html << "<a href='#{w}_test'>#{w.to_s.humanize}</a><br/>\n"
34
+ if action
35
+ w_instance = Netzke::Base.instance_by_config(Netzke::Base.session[:netzke_widgets][widget_name])
36
+ # only widget's actions starting with "api_" are accessible from outside (security)
37
+ api_action = action.to_s.index('__') ? action : "api_#{action}"
38
+ render :text => w_instance.send(api_action, params), :layout => false
39
+ else
40
+ super
89
41
  end
90
-
91
- render :text => html
92
42
  end
93
-
43
+
94
44
  end
@@ -1,26 +1,63 @@
1
1
  module Netzke
2
2
  module ActionViewExt
3
-
3
+ # Include JavaScript
4
4
  def netzke_js_include
5
- res = ""
6
-
7
- if ENV['RAILS_ENV'] == 'development'
8
- res << javascript_include_tag("/extjs/adapter/ext/ext-base.js", "/extjs/ext-all-debug.js")
9
- else
10
- res << javascript_include_tag("/extjs/adapter/ext/ext-base.js", "/extjs/ext-all.js")
11
- end
12
- res << javascript_tag( "Ext.authenticityToken = '#{form_authenticity_token}'") # forgery protection
5
+ # ExtJS
6
+ res = ENV['RAILS_ENV'] == 'development' ? javascript_include_tag("/extjs/adapter/ext/ext-base.js", "/extjs/ext-all-debug.js") : javascript_include_tag("/extjs/adapter/ext/ext-base.js", "/extjs/ext-all.js")
7
+ # Netzke (dynamically generated)
13
8
  res << javascript_include_tag("/netzke/netzke.js")
14
-
15
- res
16
9
  end
17
10
 
11
+ # Include CSS
18
12
  def netzke_css_include(theme_name = :default)
13
+ # ExtJS base
19
14
  res = stylesheet_link_tag("/extjs/resources/css/ext-all.css")
15
+ # ExtJS theming
20
16
  res << stylesheet_link_tag("/extjs/resources/css/xtheme-#{theme_name}.css") unless theme_name == :default
21
- res << stylesheet_link_tag("/netzke/netzke.css") # CSS from Netzke
17
+ # Netzke (dynamically generated)
18
+ res << stylesheet_link_tag("/netzke/netzke.css")
22
19
  res
23
20
  end
21
+
22
+ # JavaScript for all Netzke classes in this view, and Ext.onReady which renders all Netzke widgets in this view
23
+ def netzke_js
24
+ javascript_tag <<-END_OF_JAVASCRIPT
25
+ Ext.Ajax.extraParams = {authenticity_token: '#{form_authenticity_token}'}; // Rails' forgery protection... effective?
26
+ #{@content_for_netzke_js_classes}
27
+ Ext.onReady(function(){
28
+ #{@content_for_netzke_on_ready}
29
+ });
30
+ END_OF_JAVASCRIPT
31
+ end
32
+
33
+ # Wrapper for all the above. Use it in your layout.
34
+ # Params: <tt>:ext_theme</tt> - the name of ExtJS theme to apply (optional)
35
+ # E.g.:
36
+ # <%= netzke_init :ext_theme => "grey" %>
37
+ def netzke_init(params = {})
38
+ theme = params[:ext_theme] || :default
39
+ [netzke_css_include(theme), netzke_js_include, netzke_js].join("\n")
40
+ end
41
+
42
+ # Use this helper in your views to embed Netzke widgets. E.g.:
43
+ # netzke :my_grid, :widget_class_name => "GridPanel", :columns => [:id, :name, :created_at]
44
+ # On how to configure a widget, see documentation for Netzke::Base or/and specific widget
45
+ def netzke(name, config = {})
46
+ ::ActiveSupport::Deprecation.warn("widget_class_name option is deprecated. Use class_name instead", caller) if config[:widget_class_name]
47
+ class_name = config[:class_name] ||= config[:widget_class_name] || name.to_s.camelcase
48
+ config[:name] = name
49
+ Netzke::Base.reg_widget(config)
50
+ w = Netzke::Base.instance_by_config(config)
51
+ content_for :netzke_js_classes, w.js_missing_code(@rendered_classes ||= [])
52
+ content_for :netzke_on_ready, w.js_widget_instance
53
+ content_for :netzke_on_ready, w.js_widget_render
54
+
55
+ # Now mark this widget's class as rendered, so that we only generate it once per view
56
+ @rendered_classes << class_name unless @rendered_classes.include?(class_name)
57
+
58
+ # Return the html for this widget
59
+ w.js_widget_html
60
+ end
24
61
  end
25
62
  end
26
63
 
data/lib/netzke/base.rb CHANGED
@@ -42,7 +42,7 @@ module Netzke
42
42
 
43
43
  include Netzke::BaseJs # javascript (client-side)
44
44
 
45
- attr_accessor :parent, :name, :global_id, :permissions, :session
45
+ attr_accessor :parent, :name, :global_id #, :permissions, :session
46
46
 
47
47
  # Class-level Netzke::Base configuration. The defaults also get specified here.
48
48
  def self.config
@@ -152,7 +152,8 @@ module Netzke
152
152
 
153
153
  # Instance of widget by config
154
154
  def self.instance_by_config(config)
155
- widget_class = "Netzke::#{config[:widget_class_name]}".constantize
155
+ ::ActiveSupport::Deprecation.warn("widget_class_name option is deprecated. Use class_name instead", caller) if config[:widget_class_name]
156
+ widget_class = "Netzke::#{config[:class_name] || config[:widget_class_name]}".constantize
156
157
  widget_class.new(config)
157
158
  end
158
159
 
@@ -163,24 +164,22 @@ module Netzke
163
164
  nil
164
165
  end
165
166
 
166
- # Return persistent config class
167
- # def self.persistent_config
168
- # # if the class is not present, fake it (it will not store anything, and always return nil)
169
- # persistent_config_manager_class || {}
170
- # end
171
-
172
167
  # Widget initialization process
173
168
  # * the config hash is available to the widget after the "super" call in the initializer
174
169
  # * override/add new default configuration options into the "default_config" method
175
170
  # (the config hash is not yet available)
176
171
  def initialize(config = {}, parent = nil)
177
- @session = Netzke::Base.session
172
+ # @session = Netzke::Base.session
178
173
  @passed_config = config # configuration passed at the moment of instantiation
179
174
  @parent = parent
180
175
  @name = config[:name].nil? ? short_widget_class_name.underscore : config[:name].to_s
181
176
  @global_id = parent.nil? ? @name : "#{parent.global_id}__#{@name}"
182
177
  @flash = []
183
178
  end
179
+
180
+ def session
181
+ Netzke::Base.session
182
+ end
184
183
 
185
184
  #
186
185
  # Configuration
@@ -215,7 +214,7 @@ module Netzke
215
214
  def persistent_config(global = false)
216
215
  if persistent_config_enabled? || global
217
216
  config_class = self.class.persistent_config_manager_class
218
- config_class.widget_name = global ? nil : persistent_config_id # pass to the config class our unique name
217
+ config_class.widget_name = global ? nil : persistence_key.to_s # pass to the config class our unique name
219
218
  config_class
220
219
  else
221
220
  # if we can't use presistent config, all the calls to it will always return nil,
@@ -225,9 +224,11 @@ module Netzke
225
224
  end
226
225
  end
227
226
 
228
- # A string which will identify the persistent config records for this widget
229
- def persistent_config_id #:nodoc:
230
- initial_config[:persistent_config_id] || global_id
227
+ # A string which will identify NetzkePreference records for this widget.
228
+ # If <tt>persistence_key</tt> is passed, use it. Otherwise use global widget's id.
229
+ def persistence_key #:nodoc:
230
+ # initial_config[:persistence_key] ? parent.try(:persistence_key) ? "#{parent.persistence_key}__#{initial_config[:persistence_key]}".to_sym : initial_config[:persistence_key] : global_id.to_sym
231
+ initial_config[:persistence_key] ? initial_config[:persistence_key] : global_id.to_sym
231
232
  end
232
233
 
233
234
  def update_persistent_ext_config(hsh)
@@ -281,9 +282,9 @@ module Netzke
281
282
  # this method will return the following hash:
282
283
  # {:enabled => true, :layout => {:width => 100, :header => {:height => 20}}}
283
284
  def persistent_config_hash
284
- return {} if !initial_config[:persistent_config]
285
+ return {} if !initial_config[:persistent_config] || Netzke::Base.persistent_config_manager_class.nil?
285
286
 
286
- prefs = NetzkePreference.find_all_for_widget(persistent_config_id)
287
+ prefs = NetzkePreference.find_all_for_widget(persistence_key.to_s)
287
288
  res = {}
288
289
  prefs.each do |p|
289
290
  hsh_levels = p.name.split("__").map(&:to_sym)
@@ -412,7 +413,8 @@ module Netzke
412
413
  aggr = aggr.to_sym
413
414
  aggregatee_config = aggregator.aggregatees[aggr]
414
415
  raise ArgumentError, "No aggregatee '#{aggr}' defined for widget '#{aggregator.global_id}'" if aggregatee_config.nil?
415
- short_class_name = aggregatee_config[:widget_class_name]
416
+ ::ActiveSupport::Deprecation.warn("widget_class_name option is deprecated. Use class_name instead", caller) if aggregatee_config[:widget_class_name]
417
+ short_class_name = aggregatee_config[:class_name] || aggregatee_config[:widget_class_name]
416
418
  raise ArgumentError, "No widget_class_name specified for aggregatee #{aggr} of #{aggregator.global_id}" if short_class_name.nil?
417
419
  widget_class = "Netzke::#{short_class_name}".constantize
418
420
 
@@ -539,6 +541,12 @@ module Netzke
539
541
  super
540
542
  end
541
543
  end
544
+
545
+ # Register the configuration for the widget in the session, and also remember that the code for it has been rendered
546
+ def self.reg_widget(config)
547
+ session[:netzke_widgets] ||= {}
548
+ session[:netzke_widgets][config[:name]] = config
549
+ end
542
550
 
543
551
  end
544
552
  end
@@ -45,7 +45,6 @@ module Netzke
45
45
  res.merge!(:netzke_api => api_points) unless api_points.empty?
46
46
 
47
47
  # Widget class name. Needed for dynamic instantiation in javascript.
48
- # res.merge!(:widget_class_name => short_widget_class_name)
49
48
  res.merge!(:scoped_class_name => self.class.js_scoped_class_name)
50
49
 
51
50
  # Actions, toolbars and menus
@@ -94,17 +93,42 @@ module Netzke
94
93
 
95
94
  # container for rendering
96
95
  def js_widget_html
97
- %Q{<div id="#{name.to_s.split('_').join('-')}-div" style="width:800px;"></div>}
96
+ %Q{<div id="#{name.to_s.split('_').join('-')}-div"></div>}
98
97
  end
99
98
 
100
99
  #
101
- #
102
- #
103
-
104
100
  # Widget's actions, tools and menus that are loaded at the moment of instantiation
105
- def actions; nil; end
101
+
102
+ private
103
+ # Extract action names from menus and toolbars.
104
+ # E.g.:
105
+ # collect_actions(["->", {:text => "Menu", :menu => [{:text => "Submenu", :menu => [:another_button]}, "-", :a_button]}])
106
+ # => {:a_button => {:text => "A button"}, :another_button => {:text => "Another button"}}
107
+ def collect_actions(arry)
108
+ res = {}
109
+ arry.each do |item|
110
+ if item.is_a?(Hash) && menu = item[:menu]
111
+ res.merge!(collect_actions(item[:menu]))
112
+ elsif item.is_a?(Symbol)
113
+ # it's an action
114
+ res.merge!(item => {:text => item.to_s.humanize})
115
+ elsif item.is_a?(String)
116
+ # it's a string item (or maybe JS code)
117
+ else
118
+ end
119
+ end
120
+ res
121
+ end
122
+
123
+ public
124
+ # Create default actions from bbar, tbar, and fbar, which are passed in the configuration
125
+ def actions
126
+ bar_items = (ext_config[:bbar] || []) + (ext_config[:tbar] || []) + (ext_config[:fbar] || [])
127
+ bar_items.uniq!
128
+ collect_actions(bar_items)
129
+ end
130
+
106
131
  def menu; nil; end
107
- # def tools; nil; end
108
132
 
109
133
  # Little helpers
110
134
  def this; "this".l; end
@@ -179,11 +203,11 @@ module Netzke
179
203
  # Declaration of widget's class (stored in the cache storage (Ext.netzke.cache) at the client side
180
204
  # to be reused at the moment of widget instantiation)
181
205
  def js_class(cached = [])
182
- # Setting the scope.
183
- # TODO: invent an easy and error-prove way to check if adding this scope is necessary.
184
- # For now setting it each and every time (very save and not really bothering).
206
+ # Defining the scope if it isn't known yet
185
207
  res = %Q{
186
- Ext.ns("#{js_full_scope}");
208
+ if (!#{js_full_scope}) {
209
+ Ext.ns("#{js_full_scope}");
210
+ }
187
211
  }
188
212
 
189
213
  if js_inheritance?
@@ -1,14 +1,12 @@
1
1
  module Netzke
2
2
  module ControllerExtensions
3
3
  def self.included(base)
4
- base.extend ControllerClassMethods
5
4
  base.send(:before_filter, :set_session_data)
6
5
  end
7
6
 
8
7
  def set_session_data
9
8
  Netzke::Base.session = session
10
9
  session[:netzke_user_id] = defined?(current_user) ? current_user.try(:id) : nil
11
-
12
10
  # set netzke_just_logged_in and netzke_just_logged_out states (may be used by Netzke widgets)
13
11
  if session[:_netzke_next_request_is_first_after_login]
14
12
  session[:netzke_just_logged_in] = true
@@ -24,111 +22,5 @@ module Netzke
24
22
  session[:netzke_just_logged_out] = false
25
23
  end
26
24
  end
27
-
28
- def method_missing(method_name)
29
- if self.class.widget_config_storage == {}
30
- super
31
- else
32
- widget, *action = method_name.to_s.split('__')
33
- widget = widget.to_sym
34
- action = !action.empty? && action.join("__").to_sym
35
-
36
- # only widget's actions starting with "api_" are accessible from outside (security)
37
- if action
38
- api_action = action.to_s.index('__') ? action : "api_#{action}"
39
-
40
- # widget module
41
- widget_class = "Netzke::#{self.class.widget_config_storage[widget][:widget_class_name]}".constantize
42
-
43
- # instantiate the server part of the widget
44
- widget_instance = widget_class.new(self.class.widget_config_storage[widget])
45
-
46
- render :text => widget_instance.send(api_action, params), :layout => false
47
- end
48
- end
49
- end
50
-
51
- module ControllerClassMethods
52
-
53
- # widget_config_storage for all widgets
54
- def widget_config_storage
55
- @@widget_config_storage ||= {}
56
- @@widget_config_storage[self.name] ||= {} # specific for controller
57
- end
58
-
59
- #
60
- # Use this method to declare a widget in the controller
61
- #
62
- def netzke(name, config={})
63
- # which module is the widget?
64
- config[:widget_class_name] ||= name.to_s.classify
65
- config[:name] ||= name
66
-
67
- # register the widget config in the storage
68
- widget_config_storage[name] = config
69
-
70
- # provide widget helpers
71
- ActionView::Base.module_eval <<-END_EVAL, __FILE__, __LINE__
72
- def #{name}_server_instance(config = {})
73
- default_config = controller.class.widget_config_storage[:#{name}]
74
- if config.empty?
75
- # only cache when the config is empty (which means that config specified in controller is used)
76
- @widget_instance_cache ||= {}
77
- @widget_instance_cache[:#{name}] ||= Netzke::Base.instance_by_config(default_config)
78
- else
79
- # if helper is called with parameters - always return a fresh instance of widget, no caching
80
- Netzke::Base.instance_by_config(default_config.deep_merge(config))
81
- end
82
- end
83
-
84
- def #{name}_widget_instance(config = {})
85
- #{name}_server_instance(config).js_widget_instance
86
- end
87
-
88
- def #{name}_class_definition
89
- @generated_widget_classes ||= []
90
- res = #{name}_server_instance.js_missing_code(@generated_widget_classes)
91
-
92
- # prevent duplication of javascript when multiple homogeneous widgets are on the same page
93
- @generated_widget_classes += #{name}_server_instance.dependencies
94
- @generated_widget_classes.uniq!
95
- res
96
- end
97
-
98
- def #{name}_widget_html
99
- #{name}_server_instance.js_widget_html
100
- end
101
-
102
- def #{name}_widget_render
103
- #{name}_server_instance.js_widget_render
104
- end
105
-
106
- END_EVAL
107
-
108
- # add controller action which will render a simple HTML page containing the widget
109
- define_method("#{name}_test") do
110
- @widget_name = name
111
- render :inline => <<-HTML
112
- <head>
113
- <meta http-equiv="Content-type" content="text/html; charset=utf-8">
114
- <title><%= @widget_name %></title>
115
- <%= netzke_js_include %>
116
- <%= netzke_css_include %>
117
- <script type="text/javascript" charset="utf-8">
118
- <%= #{name}_class_definition %>
119
- Ext.onReady(function(){
120
- <%= #{name}_widget_instance %>
121
- <%= #{name}_widget_render %>
122
- });
123
- </script>
124
- </head>
125
- <body>
126
- <%= #{name}_widget_html %>
127
- </body>
128
- HTML
129
- end
130
- end
131
- end
132
25
  end
133
-
134
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: netzke-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5.2
4
+ version: 0.5.0
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-11-09 00:00:00 -06:00
12
+ date: 2010-01-20 00:00:00 -03:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -22,6 +22,7 @@ extensions: []
22
22
  extra_rdoc_files:
23
23
  - LICENSE
24
24
  - README.rdoc
25
+ - TODO
25
26
  files:
26
27
  - .autotest
27
28
  - .gitignore