netzke-core 0.4.5.2 → 0.5.0

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