netzke-core 0.6.5 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/CHANGELOG.rdoc +7 -0
  2. data/TODO +2 -0
  3. data/app/controllers/netzke_controller.rb +72 -20
  4. data/features/actions.feature +1 -1
  5. data/features/component_loader.feature +13 -1
  6. data/features/ext.direct.feature +32 -0
  7. data/features/i18n.feature +32 -0
  8. data/features/inheritance.feature +2 -2
  9. data/features/step_definitions/generic_steps.rb +21 -5
  10. data/features/step_definitions/touch_steps.rb +3 -0
  11. data/features/support/env.rb +4 -0
  12. data/features/support/paths.rb +6 -0
  13. data/features/touch.feature +10 -0
  14. data/javascripts/core.js +10 -15
  15. data/javascripts/ext.js +92 -1
  16. data/javascripts/touch.js +11 -0
  17. data/lib/netzke/actions.rb +13 -8
  18. data/lib/netzke/base.rb +32 -45
  19. data/lib/netzke/configuration.rb +53 -0
  20. data/lib/netzke/core/options_hash.rb +27 -0
  21. data/lib/netzke/core/version.rb +1 -1
  22. data/lib/netzke/core.rb +15 -5
  23. data/lib/netzke/core_ext/hash.rb +10 -8
  24. data/lib/netzke/inheritance.rb +31 -0
  25. data/lib/netzke/javascript.rb +45 -2
  26. data/lib/netzke/railz/action_view_ext.rb +8 -0
  27. data/lib/netzke/railz/controller_extensions.rb +1 -1
  28. data/lib/netzke/railz/engine.rb +27 -0
  29. data/lib/netzke/railz.rb +1 -0
  30. data/lib/netzke/session.rb +4 -0
  31. data/lib/netzke/state.rb +6 -6
  32. data/lib/netzke-core.rb +0 -23
  33. data/netzke-core.gemspec +27 -4
  34. data/spec/component/actions_spec.rb +2 -2
  35. data/spec/component/configuration_spec.rb +61 -0
  36. data/test/rails_app/Gemfile +1 -1
  37. data/test/rails_app/Gemfile.lock +48 -46
  38. data/test/rails_app/app/components/component_loader.rb +34 -1
  39. data/test/rails_app/app/components/ext_direct/composite.rb +48 -0
  40. data/test/rails_app/app/components/ext_direct/details.rb +13 -0
  41. data/test/rails_app/app/components/ext_direct/selector.rb +31 -0
  42. data/test/rails_app/app/components/ext_direct/statistics.rb +13 -0
  43. data/test/rails_app/app/components/extended_localized_panel.rb +2 -0
  44. data/test/rails_app/app/components/extended_server_caller.rb +0 -1
  45. data/test/rails_app/app/components/localized_panel.rb +28 -0
  46. data/test/rails_app/app/components/server_caller.rb +1 -0
  47. data/test/rails_app/app/components/server_counter.rb +123 -0
  48. data/test/rails_app/app/controllers/application_controller.rb +7 -0
  49. data/test/rails_app/config/initializers/backtrace_silencers.rb +1 -1
  50. data/test/rails_app/config/initializers/netzke.rb +1 -1
  51. data/test/rails_app/config/locales/en.yml +9 -2
  52. data/test/rails_app/config/locales/es.yml +11 -0
  53. data/test/rails_app/config/routes.rb +2 -2
  54. metadata +28 -16
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,10 @@
1
+ = v0.6.6 - 2011-02-26
2
+ * enhancements
3
+ * Client-server communication is updated to use Ext.Direct (many thanks to @pschyska)
4
+ * Introduced +js_translate+ class method that allows specifying i18n properties used in the JavaScript class
5
+ * Better handling of actions i18n
6
+ * New `Netzke::Base.class_config_option` method to specify a class-level configuration options for a component, e.g. (in GridPanel): `class_config_option :column_filters_available, true`. This option then can be set in Rails application configuration, e.g.: `config.netzke.basepack.grid_panel.column_filters_available = false`, or directly on `Netzke::Core.config`, e.g.: `Netzke::Core.config.netzke.basepack.grid_panel.column_filters_available = false`.
7
+
1
8
  = v0.6.5 - 2011-01-14
2
9
  * enhancements
3
10
  * Various fixes for IE
data/TODO CHANGED
@@ -1,9 +1,11 @@
1
+ Make :items option understand also a hash.
1
2
  Caching for netzke_controller-provided JS and CSS.
2
3
  Caching - investigate reusing (fragment?) caching of Rails.
3
4
  Use Ext.Direct in the Netzke controller.
4
5
  Let specify per API point if it will use a GET or a POST request.
5
6
 
6
7
 
8
+
7
9
  = Ideas that didn't work out
8
10
 
9
11
  == Making value from super-class accessible in the block parameters in endpoints, e.g.:
@@ -1,8 +1,5 @@
1
1
  class NetzkeController < ApplicationController
2
2
 
3
- # Collect javascripts and stylesheets from all plugins that registered it in Netzke::Core.javascripts
4
- # TODO: caching
5
- # caches_action :netzke
6
3
  def ext
7
4
  respond_to do |format|
8
5
  format.js {
@@ -69,33 +66,88 @@ class NetzkeController < ApplicationController
69
66
  end
70
67
  end
71
68
 
72
-
73
- # Main dispatcher of the HTTP requests. The URL contains the name of the component,
74
- # as well as the method of this component to be called, according to the double underscore notation.
75
- # E.g.: some_grid__post_grid_data.
76
- def method_missing(method_name)
77
- component_name, *action = method_name.to_s.split('__')
78
- component_name = component_name.to_sym
79
- action = !action.empty? && action.join("__").to_sym
80
-
81
- if action
82
- w_instance = Netzke::Base.instance_by_config(Netzke::Core.session[:netzke_components][component_name])
83
- # only component's actions starting with "endpoint_" are accessible from outside (security)
84
- endpoint_action = action.to_s.index('__') ? action : "_#{action}_ep_wrapper"
85
- render :text => w_instance.send(endpoint_action, params), :layout => false
86
- else
87
- super
69
+ # Action for Ext.Direct RPC calls
70
+ def direct
71
+ result=""
72
+ error=false
73
+ if params['_json'] # this is a batched request
74
+ params['_json'].each do |batch|
75
+ result+= result.blank? ? '[' : ', '
76
+ begin
77
+ result+=invoke_endpoint batch[:act], batch[:method].underscore, batch[:data], batch[:tid]
78
+ rescue Exception => e
79
+ Rails.logger.error "!!! Netzke: Error invoking endpoint: #{batch[:act]} #{batch[:method].underscore} #{batch[:data].inspect} #{batch[:tid]}\n"
80
+ Rails.logger.error e.message
81
+ Rails.logger.error e.backtrace
82
+ error=true
83
+ break;
84
+ end
85
+ end
86
+ result+=']'
87
+ else # this is a single request
88
+ result=invoke_endpoint params[:act], params[:method].underscore, params[:data], params[:tid]
88
89
  end
90
+ render :text => result, :layout => false, :status => error ? 500 : 200
89
91
  end
90
92
 
91
- private
93
+ # Action used by non-Ext.Direct (Touch) components
94
+ def dispatcher
95
+ endpoint_dispatch(params[:address])
96
+ end
97
+
98
+ protected
99
+
100
+ def invoke_endpoint component_name, action, data, tid
101
+ data=data[0] || {} # we get data as an array, extract the single argument if available
102
+
103
+ root_component_name, *sub_components = component_name.split('__')
104
+ root_component = Netzke::Base.instance_by_config Netzke::Core.session[:netzke_components][root_component_name.to_sym]
105
+ if sub_components.empty?
106
+ # we need to dispatch to root component, send it _#{action}_ep_wrapper
107
+ endpoint_action = "_#{action}_ep_wrapper"
108
+ else
109
+ # we need to dispatch to one or more sub_components, send subcomp__subsubcomp__endpoint to root component
110
+ endpoint_action = sub_components.join('__')+'__'+action
111
+ end
112
+ # send back JSON as specified in Ext.direct spec
113
+ # => type: rpc
114
+ # => tid, action, method as in the request, so that the client can mark the transaction and won't retry it
115
+ # => result: JavaScript code from he endpoint result which gets applied to the client-side component instance
116
+ result = root_component.send(endpoint_action, data)
117
+
118
+ {:type => "rpc", :tid => tid, :action => component_name, :method => action, :result => result.present? && result.l || {}}.to_json
119
+ end
120
+
121
+ # Main dispatcher of old-style (Sencha Touch) HTTP requests. The URL contains the name of the component,
122
+ # as well as the method of this component to be called, according to the double underscore notation.
123
+ # E.g.: some_grid__post_grid_data.
124
+ def endpoint_dispatch(method_name)
125
+ component_name, *action = method_name.to_s.split('__')
126
+ component_name = component_name.to_sym
127
+ action = !action.empty? && action.join("__").to_sym
128
+
129
+ if action
130
+ w_instance = Netzke::Base.instance_by_config(Netzke::Core.session[:netzke_components][component_name])
131
+ # only component's actions starting with "endpoint_" are accessible from outside (security)
132
+ endpoint_action = action.to_s.index('__') ? action : "_#{action}_ep_wrapper"
133
+ render :text => w_instance.send(endpoint_action, params), :layout => false
134
+ else
135
+ super
136
+ end
137
+ end
138
+
92
139
  # Generates initial javascript code that is dependent on Rails environement
93
140
  def initial_dynamic_javascript
94
141
  res = []
95
142
  res << %(Ext.Ajax.extraParams = {authenticity_token: '#{form_authenticity_token}'}; // Rails' forgery protection)
96
143
  res << %{Ext.ns('Netzke');}
144
+ res << %{Ext.ns('Netzke.core');}
97
145
  res << %{Netzke.RelativeUrlRoot = '#{ActionController::Base.config.relative_url_root}';}
98
146
  res << %{Netzke.RelativeExtUrl = '#{ActionController::Base.config.relative_url_root}/extjs';}
147
+
148
+ # TODO: this will needs some DRYing
149
+ res << %{Netzke.core.directMaxRetries = '#{Netzke::Core.js_direct_max_retries}';}
150
+
99
151
  res.join("\n")
100
152
  end
101
153
 
@@ -9,5 +9,5 @@ Scenario: Pressing button should result in corresponding actions
9
9
  Then I should see "Disabled action"
10
10
  And button "Disabled action" should be disabled
11
11
 
12
- When I press "Some action"
12
+ When I press "Some Cool Action"
13
13
  Then I should see "Some action was triggered"
@@ -14,11 +14,23 @@ Feature: Component loader
14
14
  Then I should see "Component loaded in window"
15
15
 
16
16
  @selenium
17
- Scenario: Component loader should invoke a callback
17
+ Scenario: Component loader should invoke a callback in loadComponent
18
18
  Given I am on the ComponentLoader test page
19
19
  When I press "Load with feedback"
20
20
  Then I should see "Callback invoked!"
21
21
 
22
+ @selenium
23
+ Scenario: Component loader should invoke a generic endpoint callback
24
+ Given I am on the ComponentLoader test page
25
+ When I press "Load with generic callback"
26
+ Then I should see "Generic callback invoked!"
27
+
28
+ @selenium
29
+ Scenario: Component loader should invoke a generic endpoint callback
30
+ Given I am on the ComponentLoader test page
31
+ When I press "Load with generic callback and scope"
32
+ Then I should see "Fancy title set!"
33
+
22
34
  @selenium
23
35
  Scenario: Component loader should load a window component with another component in it
24
36
  Given I am on the ComponentLoader test page
@@ -0,0 +1,32 @@
1
+ Feature: Actions
2
+ In order to value
3
+ As a role
4
+ I want feature
5
+
6
+ @javascript
7
+ Scenario: Making seven consecutive endpoint calls within 10ms batches to one XHR
8
+ When I go to the ServerCounter test page
9
+ And I press "Count seven times"
10
+ Then total requests made should be 1
11
+
12
+ @javascript
13
+ Scenario: Doing two calls to different endpoints preserves the order in request and response
14
+ When I go to the ServerCounter test page
15
+ And I press "Do ordered"
16
+ # "Second." is the indication that the result was applied to the client in the right order
17
+ # "2" is the indication that the request were processed in the right order on the server side
18
+ Then I should see "Second. 2"
19
+
20
+ @javascript
21
+ Scenario: 2 out of 5 requests fail, but the retry mechanism should recover, and process them in order
22
+ Given I go to the ServerCounter test page
23
+ When I press "Fail two out of five"
24
+ Then I should see "1,2,3,4,5"
25
+
26
+ @javascript
27
+ Scenario: Updating 3 components (in one request)
28
+ Given I go to the ExtDirect::Composite test page
29
+ When I fill in "User:" with "Power User"
30
+ And I press "Update"
31
+ Then I should see "Details for user Power User"
32
+ And I should see "Statistics for user Power User"
@@ -0,0 +1,32 @@
1
+ Feature: I18n
2
+ In order to value
3
+ As a role
4
+ I want feature
5
+
6
+ @javascript
7
+ Scenario: LocalizedPanel should be available in 2 languages
8
+ When I go to the LocalizedPanel test page
9
+ Then I should see "Localized Panel"
10
+ And I should see "First property, Second property"
11
+ And I should see "First action"
12
+ And I should see "Second action"
13
+
14
+ When I go to the "es" version of the LocalizedPanel page
15
+ Then I should see "Panel Localizada"
16
+ And I should see "Primera propriedad, Segunda propriedad"
17
+ And I should see "Primera acción"
18
+ And I should see "Segunda acción"
19
+
20
+ When I go to the "es" version of the ExtendedLocalizedPanel page
21
+ Then I should see "Panel Localizada"
22
+ And I should see "Primera propriedad, Segunda propriedad"
23
+ And I should see "Action one"
24
+ And I should see "Segunda acción"
25
+
26
+ When I go to the "en" version of the ExtendedLocalizedPanel page
27
+ Then I should see "Localized Panel"
28
+ And I should see "First property, Second property"
29
+ And I should see "Action one"
30
+ And I should see "Second action"
31
+
32
+ # NOTE: make sure that the locale is restored to "en" in the end!
@@ -5,9 +5,9 @@ Feature: Inheritance
5
5
 
6
6
  @javascript
7
7
  Scenario: Inherited component should successfully call parent methods in Ruby and JavaScript
8
- When I go to the ExtendedServerCaller test page
8
+ When I go to the "en" version of the ExtendedServerCaller page
9
9
  Then I should see "Extended Server Caller"
10
- When I press "Call server"
10
+ When I press "Call server extensively"
11
11
  Then I should see "All quiet here on the server, shiny weather"
12
12
  And I should see "Added by extended Server Caller"
13
13
 
@@ -6,10 +6,26 @@ When /^I execute "([^\"]*)"$/ do |script|
6
6
  page.driver.browser.execute_script(script)
7
7
  end
8
8
 
9
- Then /^button "([^\"]*)" should be disabled$/ do |arg1|
10
- pending
9
+ Then /^button "([^"]*)" should be enabled$/ do |arg1|
10
+ page.driver.browser.execute_script(<<-JS).should == true
11
+ var btn = Ext.ComponentMgr.all.filter('text', '#{arg1}').filter('type','button').first();
12
+ return typeof(btn)!='undefined' ? !btn.disabled : false
13
+ JS
11
14
  end
12
15
 
13
- When /^I sleep 1 second$/ do
14
- sleep 1
15
- end
16
+ Then /^button "([^"]*)" should be disabled$/ do |arg1|
17
+ page.driver.browser.execute_script(<<-JS).should == true
18
+ var btn = Ext.ComponentMgr.all.filter('text', '#{arg1}').filter('type','button').first();
19
+ return typeof(btn)!='undefined' ? btn.disabled : false
20
+ JS
21
+ end
22
+
23
+ When /^total requests made should be (\d+)$/ do |count|
24
+ page.driver.browser.execute_script(<<-JS).should == true
25
+ return Netzke.connectionCount == #{count};
26
+ JS
27
+ end
28
+
29
+ When /I sleep (\d+) seconds?/ do |arg1|
30
+ sleep arg1.to_i
31
+ end
@@ -0,0 +1,3 @@
1
+ When /^I press button "([^"]*)"$/ do |action|
2
+ pending
3
+ end
@@ -27,6 +27,10 @@ require 'cucumber/rails/capybara_javascript_emulation' # Lets you click links wi
27
27
  # steps to use the XPath syntax.
28
28
  Capybara.default_selector = :css
29
29
 
30
+ # Capybara.register_driver :selenium do |app|
31
+ # Capybara::Driver::Selenium.new(app, {:browser => :chrome } )
32
+ # end
33
+
30
34
  # If you set this to false, any error raised from within your app will bubble
31
35
  # up to your step definition and out to cucumber unless you catch it somewhere
32
36
  # on the way. You can make Rails rescue errors and render error pages on a
@@ -32,6 +32,12 @@ module NavigationHelpers
32
32
  when /the (.*) test page/
33
33
  components_path(:component => $1)
34
34
 
35
+ when /the (.*) page for touch/
36
+ touch_components_path(:component => $1)
37
+
38
+ when /the "(.+)" version of the (.*) page/
39
+ components_path(:component => $2, :locale => $1)
40
+
35
41
  else
36
42
  begin
37
43
  page_name =~ /the (.*) page/
@@ -0,0 +1,10 @@
1
+ Feature: Touch - to be run in Chrome only
2
+ In order to value
3
+ As a role
4
+ I want feature
5
+
6
+ @javascript
7
+ Scenario: Client-server communication
8
+ Given I am on the ServerCaller page for touch
9
+ When I press button "Bug server"
10
+ Then I should see "Hello from the server!"
data/javascripts/core.js CHANGED
@@ -11,9 +11,9 @@ Ext.BLANK_IMAGE_URL = Netzke.RelativeExtUrl + "/resources/images/default/s.gif";
11
11
  Ext.ns('Ext.netzke'); // namespace for extensions that depend on Ext
12
12
 
13
13
  Netzke.isLoading=function () {
14
- return Netzke.runningRequests!=0;
14
+ return Netzke.runningRequests != 0;
15
15
  }
16
- Netzke.runningRequests=0
16
+ Netzke.runningRequests = 0;
17
17
 
18
18
  Netzke.deprecationWarning = function(msg){
19
19
  if (typeof console == 'undefined') {
@@ -108,17 +108,6 @@ Netzke.componentMixin = Ext.applyIf(Netzke.classes.Core.Mixin, {
108
108
  // receiver.superclass.constructor.call(this, config);
109
109
  // },
110
110
 
111
- /*
112
- Dynamically creates methods for api points, so that we could later call them like: this.myEndpointMethod()
113
- */
114
- processEndpoints: function(){
115
- var endpoints = this.endpoints || [];
116
- endpoints.push('deliver_component'); // all Netzke components get this endpoint
117
- Ext.each(endpoints, function(intp){
118
- this[intp.camelize(true)] = function(args, callback, scope){ this.callServer(intp, args, callback, scope); }
119
- }, this);
120
- },
121
-
122
111
  /*
123
112
  Detects component placeholders in the passed object (typically, "items"),
124
113
  and merges them with the corresponding config from this.components.
@@ -217,8 +206,14 @@ Netzke.componentMixin = Ext.applyIf(Netzke.classes.Core.Mixin, {
217
206
  return this.endpointUrl(endpoint);
218
207
  },
219
208
 
209
+ // endpointUrl: function(endpoint){
210
+ // Netzke.deprecationWarning("endpointUrl() is deprecated. Use Ext.direct counterparts instead.\nFor example, specify a DirectProxy instead of HttpProxy ( proxy: new Ext.data.DirectProxy({directFn: Netzke.providers[this.id].endPoint}) ),\nor specify api instead of url config option for BasicForm ( api: { load: Netzke.providers[this.id].loadEndPoint, submit: Netzke.providers[this.id].submitEndPoint} )");
211
+ // return Netzke.RelativeUrlRoot + "/netzke/" + this.id + "__" + endpoint;
212
+ // },
213
+
214
+ // Used by Touch components
220
215
  endpointUrl: function(endpoint){
221
- return Netzke.RelativeUrlRoot + "/netzke/" + this.id + "__" + endpoint;
216
+ return Netzke.RelativeUrlRoot + "/netzke/dispatcher?address=" + this.id + "__" + endpoint;
222
217
  },
223
218
 
224
219
  // Does the call to the server and processes the response
@@ -239,10 +234,10 @@ Netzke.componentMixin = Ext.applyIf(Netzke.classes.Core.Mixin, {
239
234
  callback.apply(scope, [this.latestResult]);
240
235
  }
241
236
  }
237
+ Netzke.runningRequests--;
242
238
  },
243
239
  scope : this
244
240
  });
245
- Netzke.runningRequests--;
246
241
  },
247
242
 
248
243
  setResult: function(result) {
data/javascripts/ext.js CHANGED
@@ -15,6 +15,65 @@ Ext.state.Provider.prototype.set = function(){};
15
15
  }
16
16
  })();
17
17
 
18
+ Netzke.classes.NetzkeRemotingProvider=Ext.extend(Ext.direct.RemotingProvider,{
19
+ getCallData: function(t){
20
+ return {
21
+ act: t.action, // rails doesn't really support having a parameter named "action"
22
+ method: t.method,
23
+ data: t.data,
24
+ type: 'rpc',
25
+ tid: t.tid
26
+ }
27
+ },
28
+
29
+ addAction: function(action, methods) {
30
+ var cls = this.namespace[action] || (this.namespace[action] = {});
31
+ for(var i = 0, len = methods.length; i < len; i++){
32
+ var m = methods[i];
33
+ cls[m.name] = this.createMethod(action, m);
34
+ }
35
+ },
36
+
37
+ // process response regardess of status
38
+ // i.e. in a batch request,
39
+ // - we request tids 1,2,3.
40
+ // - server is able to process 1 but not 2
41
+ // - server will stop and *not* process 3, because it could be dependant on 2 (this best possible approach to this
42
+ // situation, as we don't have transactions)
43
+ // - server will respond with status 500, indicating a fault
44
+ // - in the response, server will respond with the result from tid 1
45
+ // - client marks tid 1 as success (deletes the transaction from pending), and will retry 2 and 3 - this is the
46
+ // change in Ext.direct.RemotingProvider's default behaviour
47
+ onData: function(opt, success, xhr){
48
+ var events=this.getEvents(xhr);
49
+
50
+ for(var i = 0, len = events.length; i < len; i++){
51
+ var e = events[i],
52
+ t = this.getTransaction(e);
53
+ this.fireEvent('data', this, e);
54
+ if(t){
55
+ this.doCallback(t, e, true);
56
+ Ext.Direct.removeTransaction(t);
57
+ }
58
+ }
59
+
60
+ Netzke.classes.NetzkeRemotingProvider.superclass.onData.call(this, opt, success, xhr);
61
+ }
62
+
63
+ });
64
+
65
+ Netzke.directProvider = new Netzke.classes.NetzkeRemotingProvider({
66
+ "type": "remoting", // create a Ext.direct.RemotingProvider
67
+ "url": Netzke.RelativeUrlRoot + "/netzke/direct/", // url to connect to the Ext.Direct server-side router.
68
+ "namespace": "Netzke.providers", // namespace to create the Remoting Provider in
69
+ "actions": {},
70
+ "maxRetries": Netzke.core.directMaxRetries,
71
+ "enableBuffer": true, // buffer/batch requests within 10ms timeframe
72
+ "timeout": 30000 // 30s timeout per request
73
+ });
74
+
75
+ Ext.Direct.addProvider(Netzke.directProvider);
76
+
18
77
  Ext.apply(Netzke.classes.Core.Mixin, {
19
78
  height: 400,
20
79
  border: false,
@@ -63,6 +122,38 @@ Ext.apply(Netzke.classes.Core.Mixin, {
63
122
  this.initComponentWithoutNetzke();
64
123
  },
65
124
 
125
+ /*
126
+ Dynamically creates methods for endpoints, so that we could later call them like: this.myEndpointMethod() (using Ext.Direct)
127
+ */
128
+ processEndpoints: function(){
129
+ var endpoints = this.endpoints || [];
130
+ endpoints.push('deliver_component'); // all Netzke components get this endpoint
131
+ var directActions = [];
132
+ var that=this;
133
+
134
+ Ext.each(endpoints, function(intp){
135
+ directActions.push({"name":intp.camelize(true), "len":1});
136
+ //this[intp.camelize(true)] = function(args, callback, scope){ this.callServer(intp, args, callback, scope); }
137
+ this[intp.camelize(true)] = function(arg, callback, scope) {
138
+ Netzke.runningRequests++;
139
+ scope=scope || that;
140
+ Netzke.providers[this.id][intp.camelize(true)].call(typeof scope != 'undefined' ? scope : that, arg, function(result, remotingEvent) {
141
+ if(remotingEvent.message) {
142
+ console.error("RPC event indicates an error: ", remotingEvent);
143
+ throw new Error(remotingEvent.message);
144
+ }
145
+ that.bulkExecute(result); // invoke the endpoint result on the calling component
146
+ if(typeof callback == "function") {
147
+ callback.call(scope, that.latestResult); // invoke the callback on the provided scope, or on the calling component if no scope set. Pass latestResult to callback
148
+ }
149
+ Netzke.runningRequests--;
150
+ });
151
+ }
152
+ }, this);
153
+
154
+ Netzke.directProvider.addAction(this.id, directActions);
155
+ },
156
+
66
157
  normalizeTools: function() {
67
158
  if (this.tools) {
68
159
  var normTools = [];
@@ -87,7 +178,7 @@ Ext.apply(Netzke.classes.Core.Mixin, {
87
178
  this.addEvents(name+'click');
88
179
 
89
180
  // Configure the action
90
- var actionConfig = this.actions[name];
181
+ var actionConfig = Ext.apply({}, this.actions[name]); // do not modify original this.actions
91
182
  actionConfig.customHandler = actionConfig.handler;
92
183
  actionConfig.handler = this.actionHandler.createDelegate(this); // handler common for all actions
93
184
  actionConfig.name = name;
data/javascripts/touch.js CHANGED
@@ -15,6 +15,17 @@ Ext.apply(Netzke.classes.Core.Mixin, {
15
15
  this.initComponentWithoutNetzke();
16
16
  },
17
17
 
18
+ /*
19
+ Dynamically creates methods for api points, so that we could later call them like: this.myEndpointMethod()
20
+ */
21
+ processEndpoints: function(){
22
+ var endpoints = this.endpoints || [];
23
+ endpoints.push('deliver_component'); // all Netzke components get this endpoint
24
+ Ext.each(endpoints, function(intp){
25
+ this[intp.camelize(true)] = function(args, callback, scope){ this.callServer(intp, args, callback, scope); }
26
+ }, this);
27
+ },
28
+
18
29
  /*
19
30
  Detects action configs in the passed object, and replaces them with instances of Ext.Action created by normalizeActions().
20
31
  This detects action in arbitrary level of nesting, which means you can put any other components in your toolbar, and inside of them specify menus/items or even toolbars.
@@ -85,16 +85,21 @@ module Netzke
85
85
  private
86
86
 
87
87
  def normalize_action_config(config)
88
- if config[:icon].is_a?(Symbol)
89
- config[:icon] = uri_to_icon(config[:icon])
90
- end
88
+ config.tap do |c|
89
+ if c[:icon].is_a?(Symbol)
90
+ c[:icon] = uri_to_icon(c[:icon])
91
+ end
91
92
 
92
- # Default text and tooltip
93
- default_text = I18n.t(i18n_id + ".actions." + config[:name], :default => config[:name].humanize)
94
- config[:text] ||= default_text
95
- config[:tooltip] ||= I18n.t(i18n_id + ".actions." + config[:name] + "_tooltip", :default => default_text)
93
+ # Default text and tooltip
94
+ c[:text] ||= c[:name].humanize
95
+ c[:tooltip] ||= c[:name].humanize
96
96
 
97
- config
97
+ # If we have an I18n for it, use it
98
+ default_text = I18n.t(i18n_id + ".actions." + c[:name], :default => "")
99
+ c[:text] = default_text if default_text.present?
100
+ default_tooltip = I18n.t(i18n_id + ".actions." + c[:name] + "_tooltip", :default => default_text)
101
+ c[:tooltip] = default_tooltip if default_tooltip.present?
102
+ end
98
103
  end
99
104
 
100
105
  def uri_to_icon(icon)
data/lib/netzke/base.rb CHANGED
@@ -3,6 +3,7 @@ require 'active_support/memoizable'
3
3
  require 'netzke/core_ext'
4
4
  require 'netzke/javascript'
5
5
  require 'netzke/stylesheets'
6
+ require 'netzke/inheritance'
6
7
  require 'netzke/services'
7
8
  require 'netzke/composition'
8
9
  require 'netzke/configuration'
@@ -15,29 +16,32 @@ module Netzke
15
16
  # The base for every Netzke component
16
17
  #
17
18
  # == Class-level configuration
18
- # You can configure any component's class as follows:
19
- # # e.g. in the initializers/netzke.rb
20
- # MyComponent.setup do |config|
21
- # config.default_instance_config = { :some_option => true }
22
- # end
19
+ # You can configure component classes in Rails Application, e.g.:
20
+ #
21
+ # config.netzke.basepack.grid_panel.column_filters_available = false
22
+ #
23
+ # Optionally, when used outside of Rails, you can also set the values directly on Netzke::Core.config (the Engine does it for you):
24
+ #
25
+ # Netzke::Core.config.netzke.basepack.grid_panel.column_filters_available = false
26
+ #
27
+ # If both default and overriding values are hashes, the default value gets deep-merged with the overriding value.
23
28
  #
24
29
  # Netzke::Base provides the following class-level configuration options:
25
- # * default_instance_config - a hash that will be used as default configuration for this component's instances
30
+ # * default_instance_config - a hash that will be used as default configuration for ALL of this component's instances.
26
31
  class Base
27
-
28
- class_attribute :default_instance_config
29
- self.default_instance_config = {}
30
-
31
32
  include Session
32
33
  include State
33
34
  include Configuration
34
35
  include Javascript
36
+ include Inheritance
35
37
  include Services
36
38
  include Composition
37
39
  include Stylesheets
38
40
  include Embedding
39
41
  include Actions
40
42
 
43
+ class_config_option :default_instance_config, {}
44
+
41
45
  # Parent component
42
46
  attr_reader :parent
43
47
 
@@ -73,42 +77,11 @@ module Netzke
73
77
  (config[:klass] || constantize_class_name(config[:class_name])).new(config)
74
78
  end
75
79
 
76
- # All ancestor classes in the Netzke class hierarchy (i.e. up to Netzke::Base)
77
- def class_ancestors
78
- if self == Netzke::Base
79
- []
80
- else
81
- superclass.class_ancestors + [self]
82
- end
83
- end
84
-
85
- # Same as +read_inheritable_attribute+ returning a hash, but returns empty hash when it's equal to superclass's
86
- def read_clean_inheritable_hash(attr_name)
87
- res = read_inheritable_attribute(attr_name) || {}
88
- # We don't want here any values from the superclass (which is the consequence of using inheritable attributes).
89
- res == self.superclass.read_inheritable_attribute(attr_name) ? {} : res
90
- end
91
-
92
- # Same as +read_inheritable_attribute+ returning a hash, but returns empty hash when it's equal to superclass's
93
- def read_clean_inheritable_array(attr_name)
94
- res = read_inheritable_attribute(attr_name) || []
95
- # We don't want here any values from the superclass (which is the consequence of using inheritable attributes).
96
- res == self.superclass.read_inheritable_attribute(attr_name) ? [] : res
80
+ # The ID used to locate this component's block in locale files
81
+ def i18n_id
82
+ name.split("::").map{|c| c.underscore}.join(".")
97
83
  end
98
- end
99
-
100
84
 
101
- def self.total_instances
102
- @@instances || 0
103
- end
104
-
105
- def self.reset_total_instances
106
- @@instances = 0
107
- end
108
-
109
- def self.increase_total_instances
110
- @@instances ||= 0
111
- @@instances += 1
112
85
  end
113
86
 
114
87
  # Instantiates a component instance. A parent can optionally be provided.
@@ -147,7 +120,21 @@ module Netzke
147
120
  end
148
121
 
149
122
  def i18n_id
150
- self.class.name.split("::").map{|c| c.underscore}.join(".")
123
+ self.class.i18n_id
124
+ end
125
+
126
+ # Used for performance measurments
127
+ def self.total_instances
128
+ @@instances || 0
129
+ end
130
+
131
+ def self.reset_total_instances
132
+ @@instances = 0
133
+ end
134
+
135
+ def self.increase_total_instances
136
+ @@instances ||= 0
137
+ @@instances += 1
151
138
  end
152
139
 
153
140
  private