netzke-core 0.12.3 → 1.0.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/CHANGELOG.md +65 -607
  2. data/Gemfile +1 -1
  3. data/LICENSE +2 -6
  4. data/README.md +77 -145
  5. data/javascripts/base.js +582 -96
  6. data/javascripts/core.js +62 -0
  7. data/javascripts/notifications.js +43 -0
  8. data/javascripts/remoting_provider.js +29 -0
  9. data/javascripts/routing.js +63 -0
  10. data/lib/netzke/base.rb +16 -83
  11. data/lib/netzke/core/action_config.rb +1 -5
  12. data/lib/netzke/core/actions.rb +59 -21
  13. data/lib/netzke/core/{client_class.rb → client_class_config.rb} +81 -73
  14. data/lib/netzke/core/client_code.rb +157 -0
  15. data/lib/netzke/core/component_config.rb +2 -2
  16. data/lib/netzke/core/composition.rb +85 -65
  17. data/lib/netzke/core/configuration.rb +26 -14
  18. data/lib/netzke/core/core_i18n.rb +17 -0
  19. data/lib/netzke/core/css_config.rb +6 -6
  20. data/lib/netzke/core/dynamic_assets.rb +17 -24
  21. data/lib/netzke/core/embedding.rb +2 -2
  22. data/lib/netzke/core/endpoint_response.rb +8 -2
  23. data/lib/netzke/core/inheritance.rb +33 -0
  24. data/lib/netzke/core/plugins.rb +1 -4
  25. data/lib/netzke/core/railz/action_view_ext.rb +1 -1
  26. data/lib/netzke/core/railz/controller_extensions.rb +21 -15
  27. data/lib/netzke/core/services.rb +61 -48
  28. data/lib/netzke/core/session.rb +0 -2
  29. data/lib/netzke/core/state.rb +11 -9
  30. data/lib/netzke/core/stylesheets.rb +3 -3
  31. data/lib/netzke/core/version.rb +1 -1
  32. data/lib/netzke/core.rb +3 -3
  33. data/lib/netzke/plugin.rb +2 -6
  34. data/stylesheets/core.css +2 -2
  35. metadata +11 -10
  36. data/TODO.md +0 -9
  37. data/javascripts/ext.js +0 -518
  38. data/lib/netzke/core/config_to_dsl_delegator.rb +0 -62
  39. data/lib/netzke/core/dsl_support.rb +0 -70
  40. data/lib/netzke/core/html.rb +0 -29
  41. data/lib/netzke/core/javascript.rb +0 -123
@@ -0,0 +1,62 @@
1
+ /*
2
+ * This file gets loaded along with the rest of Ext library at the initial load
3
+ * At this time the following constants have been set by Rails:
4
+ *
5
+ * * Netzke.RelativeUrlRoot - set to ActionController::Base.config.relative_url_root
6
+ * * Netzke.RelativeExtUrl - URL to ext files
7
+ * * Netzke.ControllerUrl - NetzkeController URL
8
+ */
9
+
10
+ Ext.ns('Ext.netzke'); // namespace for extensions that depend on Ext JS
11
+ Ext.ns('Netzke.page'); // namespace for all component instances on the page
12
+ Ext.ns('Netzke.classes'); // namespace for component classes
13
+
14
+ Netzke.warning = function(msg){
15
+ if (typeof console != 'undefined') {
16
+ console.info("Netzke: " + msg);
17
+ }
18
+ };
19
+
20
+ Netzke.deprecationWarning = Netzke.warning;
21
+
22
+ Netzke.exception = function(msg) {
23
+ throw("Netzke: " + msg);
24
+ };
25
+
26
+ // Check Ext JS version: both major and minor versions must be the same
27
+ (function(){
28
+ var requiredVersionMajor = 5,
29
+ requiredVersionMinor = 1,
30
+ extVersion = Ext.getVersion('extjs'),
31
+ currentVersionMajor = extVersion.getMajor(),
32
+ currentVersionMinor = extVersion.getMinor(),
33
+ requiredString = "" + requiredVersionMajor + "." + requiredVersionMinor + ".x";
34
+
35
+ if (requiredVersionMajor != currentVersionMajor || requiredVersionMinor != currentVersionMinor) {
36
+ Netzke.warning("Ext JS " + requiredString + " required (you have " + extVersion.toString() + ").");
37
+ }
38
+ })();
39
+
40
+ // Netzke global event emitter
41
+ Ext.define('Netzke.GlobalEvents', {
42
+ extend: 'Ext.mixin.Observable',
43
+ singleton: true
44
+ });
45
+
46
+ // xtypes of cached Netzke classes
47
+ Netzke.cache = [];
48
+
49
+ // Because of Netzke's double-underscore notation, Ext.TabPanel should have a different id-delimiter (yes, this must be in netzke-core)
50
+ Ext.TabPanel.prototype.idDelimiter = "___";
51
+
52
+ // Enable quick tips
53
+ Ext.QuickTips.init();
54
+
55
+ // Used in testing
56
+ if( Netzke._pendingRequests == undefined ){
57
+ Netzke._pendingRequests=0;
58
+ Ext.Ajax.on('beforerequest', function(conn, opt) { Netzke._pendingRequests += 1; });
59
+ Ext.Ajax.on('requestcomplete', function(conn, opt) { Netzke._pendingRequests -= 1; });
60
+ Ext.Ajax.on('requestexception', function(conn, opt) { Netzke._pendingRequests -= 1; });
61
+ Netzke.ajaxIsLoading = function() { return Netzke._pendingRequests > 0; };
62
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Class creating simple notifications. Borrowed from http://dev.sencha.com/extjs/5.1.0/examples/shared/examples.js
3
+ * @class Netzke.Notifier
4
+ */
5
+ Ext.define('Netzke.Notifier', function(){
6
+ var msgCt;
7
+
8
+ function createBox(t, s){
9
+ return t ?
10
+ '<div class="msg ' + Ext.baseCSSPrefix + 'border-box"><h3>' + t + '</h3><p>' + s + '</p></div>'
11
+ :
12
+ '<div class="msg ' + Ext.baseCSSPrefix + 'border-box"><p>' + s + '</p></div>';
13
+ }
14
+
15
+
16
+ return {
17
+ /**
18
+ * Shows notification on the screen.
19
+ * @method msg
20
+ * @param {String} msg Notification body HTML
21
+ * @param {Object} options May contain the following keys:
22
+ *
23
+ * * **title** - title of notification
24
+ * * **delay** (ms) - time notification should stay on the screen
25
+ */
26
+ msg: function(msg, options){
27
+ if (options == undefined) options = {};
28
+
29
+ if (Ext.isArray(msg)) {
30
+ msg = msg.join("<br>")
31
+ }
32
+
33
+ if (msgCt) {
34
+ document.body.appendChild(msgCt.dom);
35
+ } else {
36
+ msgCt = Ext.DomHelper.append(document.body, {id:'msg-div'}, true);
37
+ }
38
+ var m = Ext.DomHelper.append(msgCt, createBox(options.title, msg), true);
39
+ m.hide();
40
+ m.slideIn('t').ghost("t", { delay: options.delay || Netzke.Core.NotificationDelay, remove: true});
41
+ }
42
+ };
43
+ });
@@ -0,0 +1,29 @@
1
+ Ext.define('Netzke.NetzkeRemotingProvider', {
2
+
3
+ extend: 'Ext.direct.RemotingProvider',
4
+ url: Netzke.ControllerUrl + "direct/", // url to connect to the Ext.Direct server-side router.
5
+ namespace: "Netzke.remotingMethods", // will have a key per Netzke component, each mapped to a hash with a RemotingMethod per endpoint
6
+ maxRetries: Netzke.Core.directMaxRetries,
7
+ enableBuffer: true, // buffer/batch requests within 10ms timeframe
8
+ timeout: 30000, // 30s timeout per request
9
+
10
+ getPayload: function(t){
11
+ return {
12
+ path: t.action,
13
+ endpoint: t.method,
14
+ data: t.data[0],
15
+ tid: t.id,
16
+ type: 'rpc'
17
+ }
18
+ },
19
+
20
+ // Adds remoting method to component
21
+ addRemotingMethodToComponent: function(componentConfig, methodName) {
22
+ var cls = this.namespace[componentConfig.id] || (this.namespace[componentConfig.id] = {});
23
+ var method = Ext.create('Ext.direct.RemotingMethod', {name: methodName, len: 1});
24
+ cls[methodName] = this.createHandler(componentConfig.path, method);
25
+ }
26
+ });
27
+
28
+ Netzke.directProvider = Ext.create(Netzke.NetzkeRemotingProvider);
29
+ Ext.Direct.addProvider(Netzke.directProvider);
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Routing override for Netzke.Base.
3
+ * @class Netzke.Core.Routing
4
+ */
5
+ Ext.define('Netzke.Core.Routing', {
6
+ override: 'Netzke.Base',
7
+
8
+ netzkeAfterInitComponent: function(){
9
+ if (this.netzkeRoutes) {
10
+ var routes = this.netzkeGetRoutes();
11
+ this.netzkeRouter = Ext.create('Ext.app.Controller', { routes: this.netzkeGetRoutes() });
12
+ this.on('beforedestroy', this.netzkeCleanRoutes, this);
13
+
14
+ this.on('render', function(){
15
+ this.netzkeTriggerInitialRoute();
16
+ });
17
+ }
18
+
19
+ this.callParent();
20
+ },
21
+
22
+ /**
23
+ * Navigate to new hash route.
24
+ * @method netzkeNavigateTo
25
+ * @param route {String} Route
26
+ * @param {Object} [options] Options:
27
+ * * **append** {Boolean} append to the current route
28
+ *
29
+ * @example
30
+ *
31
+ * this.netzkeNavigateTo('user/1', {append: true})
32
+ */
33
+ netzkeNavigateTo: function(route, options){
34
+ options = options || {};
35
+ var newRoute = route;
36
+ if (options.append) {
37
+ newRoute = Ext.util.History.getToken() + "/" + newRoute;
38
+ }
39
+ Ext.util.History.add(newRoute);
40
+ },
41
+
42
+ // private
43
+
44
+ netzkeCleanRoutes: function(){
45
+ this.netzkeRouter.destroy();
46
+ },
47
+
48
+ netzkeTriggerInitialRoute: function(){
49
+ var initToken = Ext.util.History.getToken();
50
+ if (initToken) this.netzkeRouter.redirectTo(initToken, true);
51
+ },
52
+
53
+ netzkeGetRoutes: function(){
54
+ var out = {};
55
+ for (var route in this.netzkeRoutes) {
56
+ var handlerName = this.netzkeRoutes[route],
57
+ handler = this[handlerName];
58
+ if (!handler) Netzke.exception("Route handler " + handlerName + " is not defined");
59
+ out[route] = handler.bind(this);
60
+ }
61
+ return out;
62
+ }
63
+ });
data/lib/netzke/base.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'active_support/core_ext'
2
2
  require 'netzke/core/ruby_ext'
3
- require 'netzke/core/dsl_support'
4
- require 'netzke/core/javascript'
3
+ require 'netzke/core/client_code'
5
4
  require 'netzke/core/stylesheets'
6
5
  require 'netzke/core/services'
7
6
  require 'netzke/core/composition'
@@ -11,17 +10,20 @@ require 'netzke/core/state'
11
10
  require 'netzke/core/embedding'
12
11
  require 'netzke/core/actions'
13
12
  require 'netzke/core/session'
14
- require 'netzke/core/html' if Module.const_defined?(:Haml)
13
+ require 'netzke/core/core_i18n'
14
+ require 'netzke/core/inheritance'
15
15
 
16
16
  module Netzke
17
17
  # The base class for every Netzke component. Its main responsibilities include:
18
- # * JavaScript class generation and inheritance (using Ext JS class system) which reflects the Ruby class inheritance (see {Netzke::Core::Javascript})
18
+ # * Client class generation and inheritance (using Ext JS class system) which reflects the Ruby class inheritance (see {Netzke::Core::ClientCode})
19
19
  # * Nesting and dynamic loading of child components (see {Netzke::Core::Composition})
20
20
  # * Ruby-side action declaration (see {Netzke::Actions})
21
21
  # * I18n
22
22
  # * Client-server communication (see {Netzke::Core::Services})
23
23
  # * Session-based persistence (see {Netzke::Core::State})
24
24
  #
25
+ # Client-side methods are documented [here](http://api.netzke.org/client/classes/Netzke.Base.html).
26
+ #
25
27
  # == Referring to JavaScript configuration methods from Ruby
26
28
  #
27
29
  # Netzke allows use Ruby symbols for referring to pre-defined pieces of configuration. Let's say for example, that a toolbar needs to nest a control more complex than a button (say, a date field), and a component should still make it possible to make it's presence and position in the toolbar configurable. We can implement it like this:
@@ -33,7 +35,7 @@ module Netzke
33
35
  # c.tbar = [:do_something, :date_selector]
34
36
  # end
35
37
  #
36
- # While :do_something here is referring to a usual Netzke action, :date_selector is not declared in actions. If our JavaScript mixin file contains a method called `dateSelectorConfig`, it will be executed at the moment of configuring `tbar` at client side, and it's result, a config object, will substitute `date_selector`:
38
+ # While :do_something here is referring to a usual Netzke action, :date_selector is not declared in actions. If our JavaScript include file contains a method called `dateSelectorConfig`, it will be executed at the moment of configuring `tbar` at client side, and it's result, a config object, will substitute `date_selector`:
37
39
  #
38
40
  # {
39
41
  # dateSelectorConfig: function(config){
@@ -45,73 +47,23 @@ module Netzke
45
47
  #
46
48
  # This doesn't necessarily have to be used in toolbars, but also in other places in config (i.e. layouts).
47
49
  class Base
48
- include Core::DslSupport
49
50
  include Core::Session
50
51
  include Core::State
51
52
  include Core::Configuration
52
- include Core::Javascript
53
+ include Core::ClientCode
53
54
  include Core::Services
54
55
  include Core::Composition
55
56
  include Core::Plugins
56
57
  include Core::Stylesheets
57
58
  include Core::Embedding
58
59
  include Core::Actions
59
- include Core::Html if const_defined? :Haml
60
-
61
- # TODO: get rid of it
62
- class_attribute :default_instance_config
63
- self.default_instance_config = {}
64
-
65
- # set during initializations
66
- mattr_accessor :session
67
- mattr_accessor :controller
68
- mattr_accessor :logger
69
-
70
- # Parent component
71
- attr_reader :parent
72
-
73
- # Name that the parent can reference us by
74
- attr_reader :name
75
-
76
- # Global id in the component tree, following the double-underscore notation, e.g. +books__config_panel__form+
77
- attr_reader :js_id
78
-
79
- # JS id in the context of the parent
80
- attr_reader :item_id
81
-
82
- # Component's path in the component tree
83
- attr_reader :path
84
-
85
- class << self
86
- # Instance of component by config
87
- def instance_by_config(config)
88
- klass = config[:klass] || config[:class_name].constantize
89
- klass.new(config)
90
- end
91
-
92
- # The ID used to locate this component's block in locale files
93
- def i18n_id
94
- name.split("::").map{|c| c.underscore}.join(".")
95
- end
60
+ include Core::CoreI18n
61
+ include Core::Inheritance
96
62
 
97
- # Do class-level config of a component, e.g.:
98
- #
99
- # Netzke::Basepack::GridPanel.setup do |c|
100
- # c.rows_reordering_available = false
101
- # end
102
- def self.setup
103
- yield self
104
- end
63
+ # These are set during initialization
64
+ mattr_accessor :session, :controller, :logger
105
65
 
106
- # Ancestor classes in the Netzke class hierarchy up to (and excluding) +Netzke::Base+, including self; in comparison to Ruby's own Class.ancestors, the order is reversed.
107
- def netzke_ancestors
108
- if self == Netzke::Base
109
- []
110
- else
111
- superclass.netzke_ancestors + [self]
112
- end
113
- end
114
- end
66
+ attr_reader :parent, :name, :item_id, :path
115
67
 
116
68
  # Instantiates a component instance. A parent can optionally be provided.
117
69
  def initialize(conf = {}, parent = nil)
@@ -130,37 +82,18 @@ module Netzke
130
82
  # Full JS id will be built using these along the +@path+
131
83
  @item_id = conf[:item_id] || @name
132
84
 
133
- # JS full ID. Similar to +path+, but composed of item_id's. Differs from @path when multiple instances are being loaded.
134
- @js_id = conf[:js_id]
135
- @js_id ||= parent.nil? ? @item_id : [parent.js_id, @item_id].join("__")
136
-
137
- # TODO: get rid of this in 0.9
138
- @flash = []
139
-
140
85
  # Make +client_config+ accessible in +configure+ before calling +super+
141
86
  config.client_config = (conf.delete(:client_config) || {}).symbolize_keys
142
87
 
143
88
  # Build complete component configuration
144
89
  configure(config)
145
90
 
146
- normalize_config
147
-
148
- # Check whether the config is valid
91
+ # Check whether the config is valid (as specified in a custom override)
149
92
  validate_config(config)
150
- end
151
-
152
- def i18n_id
153
- self.class.i18n_id
154
- end
155
93
 
156
- private
94
+ normalize_config
157
95
 
158
- # TODO: get rid of this in 0.9
159
- def flash(flash_hash)
160
- level = flash_hash.keys.first
161
- raise "Unknown message level for flash" unless %(notice warning error).include?(level.to_s)
162
- @flash << {:level => level, :msg => flash_hash[level]}
96
+ config.deep_freeze
163
97
  end
164
-
165
98
  end
166
99
  end
@@ -22,12 +22,8 @@ module Netzke::Core
22
22
  self.icon = @icon.to_sym if @icon.present?
23
23
  end
24
24
 
25
- def icon=(path)
26
- self[:icon] = path.is_a?(Symbol) ? Netzke::Base.uri_to_icon(path) : path
27
- end
28
-
29
- # later
30
25
  def set_defaults!
26
+ self[:icon] = icon.is_a?(Symbol) ? Netzke::Base.uri_to_icon(icon) : icon
31
27
  end
32
28
 
33
29
  private
@@ -1,5 +1,5 @@
1
1
  module Netzke::Core
2
- # Netzke components allow specifying Ext actions (see http://docs.sencha.com/ext-js/4-1/#!/api/Ext.Action) in Ruby code.
2
+ # Netzke components provide for convenient configuration of Ext JS actions from the Ruby class.
3
3
  #
4
4
  # == Defining actions
5
5
  #
@@ -9,21 +9,26 @@ module Netzke::Core
9
9
  # c.text = "Destroy!"
10
10
  # c.tooltip = "Destroying it all"
11
11
  # c.icon = :delete
12
- # c.handler = :destroy_something # destroySomething will be called on JavaScript side
12
+ # c.handler = :destroy_something # `this.destroySomething` will be called on client side
13
13
  # end
14
14
  #
15
15
  # All config settings for an action are optional. When omitted, the locale files will be consulted first (see "I18n of actions"), falling back to the defaults.
16
16
  #
17
- # [+text+]
18
- # The text of the action (defaults to humanized action name)
19
- # [+icon+]
20
- # Can be set to either a String (which will be interpreted as a full URI to the icon file), or as a Symbol, which will be expanded to +Netzke::Core.icons_uri+ + "/(icon).png". Defaults to nil (no icon)
21
- # [+tooltip+]
22
- # The tooltip of the action (defaults to humanized action name)
23
- # [+disabled+]
17
+ # [text]
18
+ # The text of the action (defaults to localized action text, see on I18n below)
19
+ #
20
+ # [icon]
21
+ # Can be set to either a String (which will be interpreted as a full URI to the icon file), or as a Symbol, which will be expanded to +Netzke::Core.icons_uri+ + "/(icon).png". Defaults to localized action icon (see on I18n below) or nil (no icon)
22
+ #
23
+ # [tooltip]
24
+ # The tooltip of the action (defaults to localized action tooltip, see on I18n below)
25
+ #
26
+ # [disabled]
24
27
  # When set to +true+, renders this action as disabled
25
- # [+handler+]
26
- # A symbol that represents the JavaScript public method (snake-case), which will be called in the scope of the component instance. Defaults to +on_(action_name)+, which on JavaScript side will result in a call to +on(CamelCaseActionName)+
28
+ #
29
+ # [handler]
30
+ # A symbol that represents the JavaScript public method (snake-cased), which will be called in the scope of the component instance. Defaults to +netzke_on_{action_name}+, which on JavaScript side will result in a call to +netzkeOn{ActionName}+
31
+ #
27
32
  # [+excluded+]
28
33
  # When set to true, gets the action excluded from menus and toolbars
29
34
  #
@@ -95,24 +100,53 @@ module Netzke::Core
95
100
  #
96
101
  # == Interfering with action events in client class
97
102
  #
98
- # For each action Netzke creates an event on the level of the parent component following the convention '<action_name>click'. The handler receives the component itself as a parameter. If the handler returns +false+, the action event is not further propagated.
103
+ # For each action Netzke creates an event on the level of the parent component following the convention `<action_name>click`. The handler receives the component itself as a parameter. If the handler returns +false+, the action event is not further propagated.
104
+ #
105
+ # == Preventing name clashing with child components
106
+ #
107
+ # If a component has an action and a child component defined with the same name, referring to them by symbols in the
108
+ # configuration will result in a name clash. See {Core::Composition} on how to address that.
99
109
  module Actions
100
110
  extend ActiveSupport::Concern
101
111
 
102
112
  included do
103
- # Declares Base.action, for declaring actions, and Base#actions, which returns a [Hash] of all action configs by name
104
- declare_dsl_for :actions, config_class: Netzke::Core::ActionConfig
113
+ class_attribute :_declared_actions
114
+ self._declared_actions = []
105
115
  end
106
116
 
107
117
  module ClassMethods
108
- # Must stay public, used from ActionConfig
118
+ # Declares an action
119
+ def action(*args, &block)
120
+ args.each{|name| action(name)} if args.length > 1
121
+
122
+ name = args.first
123
+
124
+ define_method :"#{name}_action", &(block || ->(c){c})
125
+ # NOTE: "<<" won't work here as this will mutate the array shared between classes
126
+ self._declared_actions += [name]
127
+ end
128
+
109
129
  # @return [String|nil] full URI to an icon file by its name (provided we have a controller)
130
+ # NOTE: must stay public, used from ActionConfig
110
131
  def uri_to_icon(icon)
111
132
  Netzke::Core.with_icons ? [(controller && controller.config.relative_url_root), Netzke::Core.icons_uri, '/', icon.to_s, ".png"].join : nil
112
133
  end
113
134
  end
114
135
 
115
- def js_configure(c)
136
+ def actions
137
+ return @actions if @actions
138
+
139
+ @actions = {}.tap do |res|
140
+ self.class._declared_actions.each do |name|
141
+ cfg = Netzke::Core::ActionConfig.new(name, self)
142
+ send("#{name}_action", cfg)
143
+ cfg.set_defaults!
144
+ res[name.to_sym] = cfg.excluded ? {excluded: true} : cfg
145
+ end
146
+ end
147
+ end
148
+
149
+ def configure_client(c)
116
150
  super
117
151
  c.actions = actions
118
152
  end
@@ -125,19 +159,23 @@ module Netzke::Core
125
159
 
126
160
  def detect_and_normalize_action(item)
127
161
  item = {action: item} if item.is_a?(Symbol) && actions[item]
128
- if item.is_a?(Hash) && action_name = item[:action]
129
- cfg = actions[action_name]
130
- cfg.merge!(item)
131
- if cfg[:excluded]
162
+
163
+ if action_name = action_name_if_action(item)
164
+ action_config = actions[action_name]
165
+ if action_config[:excluded]
132
166
  {excluded: true}
133
167
  else
134
- item.merge(netzke_action: cfg[:action])
168
+ {action: action_name.to_s.camelize(:lower)}
135
169
  end
136
170
  else
137
171
  item
138
172
  end
139
173
  end
140
174
 
175
+ def action_name_if_action(item)
176
+ item.is_a?(Hash) && item[:action]
177
+ end
178
+
141
179
  def uri_to_icon(icon)
142
180
  self.class.uri_to_icon(icon)
143
181
  end