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.
- data/CHANGELOG.rdoc +7 -0
- data/TODO +2 -0
- data/app/controllers/netzke_controller.rb +72 -20
- data/features/actions.feature +1 -1
- data/features/component_loader.feature +13 -1
- data/features/ext.direct.feature +32 -0
- data/features/i18n.feature +32 -0
- data/features/inheritance.feature +2 -2
- data/features/step_definitions/generic_steps.rb +21 -5
- data/features/step_definitions/touch_steps.rb +3 -0
- data/features/support/env.rb +4 -0
- data/features/support/paths.rb +6 -0
- data/features/touch.feature +10 -0
- data/javascripts/core.js +10 -15
- data/javascripts/ext.js +92 -1
- data/javascripts/touch.js +11 -0
- data/lib/netzke/actions.rb +13 -8
- data/lib/netzke/base.rb +32 -45
- data/lib/netzke/configuration.rb +53 -0
- data/lib/netzke/core/options_hash.rb +27 -0
- data/lib/netzke/core/version.rb +1 -1
- data/lib/netzke/core.rb +15 -5
- data/lib/netzke/core_ext/hash.rb +10 -8
- data/lib/netzke/inheritance.rb +31 -0
- data/lib/netzke/javascript.rb +45 -2
- data/lib/netzke/railz/action_view_ext.rb +8 -0
- data/lib/netzke/railz/controller_extensions.rb +1 -1
- data/lib/netzke/railz/engine.rb +27 -0
- data/lib/netzke/railz.rb +1 -0
- data/lib/netzke/session.rb +4 -0
- data/lib/netzke/state.rb +6 -6
- data/lib/netzke-core.rb +0 -23
- data/netzke-core.gemspec +27 -4
- data/spec/component/actions_spec.rb +2 -2
- data/spec/component/configuration_spec.rb +61 -0
- data/test/rails_app/Gemfile +1 -1
- data/test/rails_app/Gemfile.lock +48 -46
- data/test/rails_app/app/components/component_loader.rb +34 -1
- data/test/rails_app/app/components/ext_direct/composite.rb +48 -0
- data/test/rails_app/app/components/ext_direct/details.rb +13 -0
- data/test/rails_app/app/components/ext_direct/selector.rb +31 -0
- data/test/rails_app/app/components/ext_direct/statistics.rb +13 -0
- data/test/rails_app/app/components/extended_localized_panel.rb +2 -0
- data/test/rails_app/app/components/extended_server_caller.rb +0 -1
- data/test/rails_app/app/components/localized_panel.rb +28 -0
- data/test/rails_app/app/components/server_caller.rb +1 -0
- data/test/rails_app/app/components/server_counter.rb +123 -0
- data/test/rails_app/app/controllers/application_controller.rb +7 -0
- data/test/rails_app/config/initializers/backtrace_silencers.rb +1 -1
- data/test/rails_app/config/initializers/netzke.rb +1 -1
- data/test/rails_app/config/locales/en.yml +9 -2
- data/test/rails_app/config/locales/es.yml +11 -0
- data/test/rails_app/config/routes.rb +2 -2
- 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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
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
|
|
data/features/actions.feature
CHANGED
@@ -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
|
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
|
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 "([
|
10
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
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
|
data/features/support/env.rb
CHANGED
@@ -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
|
data/features/support/paths.rb
CHANGED
@@ -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.
|
data/lib/netzke/actions.rb
CHANGED
@@ -85,16 +85,21 @@ module Netzke
|
|
85
85
|
private
|
86
86
|
|
87
87
|
def normalize_action_config(config)
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
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
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
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
|
-
#
|
77
|
-
def
|
78
|
-
|
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.
|
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
|