netzke-core 0.12.3 → 1.0.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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