netzke-core 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +1 -1
- data/.gitignore +4 -2
- data/CHANGELOG.rdoc +37 -15
- data/README.rdoc +93 -28
- data/Rakefile +5 -7
- data/TODO +24 -3
- data/app/controllers/netzke_controller.rb +70 -0
- data/app/models/netzke_preference.rb +170 -0
- data/autotest/discover.rb +2 -3
- data/features/actions.feature +13 -0
- data/features/basic.feature +7 -0
- data/features/client-server.feature +15 -0
- data/features/complex_component.feature +18 -0
- data/features/component.feature +13 -0
- data/features/component_loader.feature +31 -0
- data/features/composition.feature +32 -0
- data/features/custom_css.feature +17 -0
- data/features/file_inclusion.feature +13 -0
- data/features/inheritance.feature +19 -0
- data/features/persistence.feature +16 -0
- data/features/scopes.feature +17 -0
- data/features/step_definitions/custom_css_steps.rb +7 -0
- data/features/step_definitions/generic_steps.rb +15 -0
- data/features/step_definitions/web_steps.rb +219 -0
- data/features/support/env.rb +62 -0
- data/features/support/paths.rb +45 -0
- data/generators/netzke_core/USAGE +3 -3
- data/generators/netzke_core/netzke_core_generator.rb +8 -0
- data/install.rb +1 -1
- data/javascripts/core.js +542 -499
- data/lib/generators/migration_helper.rb +32 -0
- data/lib/generators/netzke/USAGE +9 -0
- data/lib/generators/netzke/core_generator.rb +24 -0
- data/lib/netzke/actions.rb +102 -0
- data/lib/netzke/base.rb +77 -529
- data/lib/netzke/composition.rb +251 -0
- data/lib/netzke/configuration.rb +134 -0
- data/lib/netzke/core/masquerading.rb +34 -0
- data/lib/netzke/core/session.rb +30 -0
- data/lib/netzke/core/version.rb +11 -0
- data/lib/netzke/core.rb +53 -0
- data/lib/netzke/core_ext/array.rb +30 -0
- data/lib/netzke/core_ext/hash.rb +84 -0
- data/lib/netzke/core_ext/string.rb +26 -0
- data/lib/netzke/core_ext/symbol.rb +13 -0
- data/lib/netzke/core_ext/time_with_zone.rb +7 -0
- data/lib/netzke/core_ext.rb +5 -141
- data/lib/netzke/embedding.rb +21 -0
- data/lib/netzke/ext_component.rb +25 -0
- data/lib/netzke/javascript.rb +248 -0
- data/lib/netzke/persistence.rb +118 -0
- data/lib/netzke/rails/action_view_ext.rb +103 -0
- data/lib/netzke/{controller_extensions.rb → rails/controller_extensions.rb} +7 -2
- data/lib/netzke/rails/routes.rb +7 -0
- data/lib/netzke/services.rb +68 -0
- data/lib/netzke/session.rb +35 -0
- data/lib/netzke/stylesheets.rb +52 -0
- data/lib/netzke-core.rb +28 -29
- data/netzke-core.gemspec +253 -0
- data/spec/component/actions_spec.rb +94 -0
- data/spec/component/base_spec.rb +25 -0
- data/spec/component/composition_spec.rb +132 -0
- data/spec/component/javascript_spec.rb +15 -0
- data/spec/core_ext_spec.rb +16 -0
- data/spec/spec.opt +2 -0
- data/spec/spec_helper.rb +35 -0
- data/{test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb → templates/core/create_netzke_preferences.rb} +4 -4
- data/test/rails_app/.gitignore +4 -0
- data/test/rails_app/Gemfile +36 -0
- data/test/rails_app/Gemfile.lock +137 -0
- data/test/rails_app/README +15 -0
- data/test/rails_app/Rakefile +7 -0
- data/test/rails_app/app/components/border_layout_panel.rb +4 -0
- data/test/rails_app/app/components/component_loader.rb +59 -0
- data/test/rails_app/app/components/component_with_actions.rb +58 -0
- data/test/rails_app/app/components/component_with_custom_css.rb +8 -0
- data/test/rails_app/app/components/component_with_included_js.rb +16 -0
- data/test/rails_app/app/components/component_with_session_persistence.rb +25 -0
- data/test/rails_app/app/components/custom.css +3 -0
- data/test/rails_app/app/components/deprecated/server_caller.rb +20 -0
- data/test/rails_app/app/components/extended_component_with_actions.rb +5 -0
- data/test/rails_app/app/components/extended_server_caller.rb +18 -0
- data/test/rails_app/app/components/included.js +5 -0
- data/test/rails_app/app/components/kinda_complex_component/basic_stuff.rb +35 -0
- data/test/rails_app/app/components/kinda_complex_component/extra_stuff.rb +16 -0
- data/test/rails_app/app/components/kinda_complex_component.rb +7 -0
- data/test/rails_app/app/components/loader_of_component_with_custom_css.rb +15 -0
- data/test/rails_app/app/components/scoped_components/deep_scoped_components/some_deep_scoped_component.rb +7 -0
- data/test/rails_app/app/components/scoped_components/extended_scoped_component.rb +5 -0
- data/test/rails_app/app/components/scoped_components/some_scoped_component.rb +5 -0
- data/test/rails_app/app/components/server_caller.rb +21 -0
- data/test/rails_app/app/components/simple_component.rb +5 -0
- data/test/rails_app/app/components/simple_tab_panel.rb +27 -0
- data/test/rails_app/app/components/simple_window.rb +3 -0
- data/test/rails_app/app/components/some_composite.rb +65 -0
- data/test/rails_app/app/components/some_ext_component.rb +8 -0
- data/test/{app_root → rails_app}/app/controllers/application_controller.rb +1 -0
- data/test/rails_app/app/controllers/components_controller.rb +12 -0
- data/test/rails_app/app/controllers/multiple_components_controller.rb +2 -0
- data/test/rails_app/app/controllers/welcome_controller.rb +5 -0
- data/test/rails_app/app/helpers/application_helper.rb +2 -0
- data/test/rails_app/app/views/layouts/application.html.erb +13 -0
- data/test/rails_app/app/views/multiple_components/set_one.html.erb +3 -0
- data/test/rails_app/config/application.rb +45 -0
- data/test/rails_app/config/boot.rb +13 -0
- data/test/rails_app/config/database.yml +22 -0
- data/test/rails_app/config/environment.rb +6 -0
- data/test/rails_app/config/environments/development.rb +22 -0
- data/test/rails_app/config/environments/production.rb +49 -0
- data/test/rails_app/config/environments/test.rb +35 -0
- data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/test/rails_app/config/initializers/inflections.rb +10 -0
- data/test/rails_app/config/initializers/mime_types.rb +5 -0
- data/test/rails_app/config/initializers/netzke.rb +3 -0
- data/test/rails_app/config/initializers/secret_token.rb +7 -0
- data/test/rails_app/config/initializers/session_store.rb +8 -0
- data/test/rails_app/config/locales/en.yml +5 -0
- data/test/rails_app/config/routes.rb +64 -0
- data/test/rails_app/config.ru +4 -0
- data/test/rails_app/db/development_structure.sql +4 -0
- data/{generators/netzke_core/templates/create_netzke_preferences.rb → test/rails_app/db/migrate/20100905214933_create_netzke_preferences.rb} +1 -3
- data/test/rails_app/db/schema.rb +24 -0
- data/test/rails_app/db/seeds.rb +7 -0
- data/test/{app_root/config/environments/in_memory.rb → rails_app/lib/tasks/.gitkeep} +0 -0
- data/test/rails_app/public/404.html +26 -0
- data/test/rails_app/public/422.html +26 -0
- data/test/rails_app/public/500.html +26 -0
- data/test/{app_root/config/environments/mysql.rb → rails_app/public/favicon.ico} +0 -0
- data/test/rails_app/public/images/rails.png +0 -0
- data/test/rails_app/public/robots.txt +5 -0
- data/test/rails_app/script/rails +6 -0
- data/test/{app_root/config/environments/postgresql.rb → rails_app/vendor/plugins/.gitkeep} +0 -0
- data/test/unit/netzke_core_test.rb +74 -75
- data/test/unit/netzke_preference_test.rb +2 -2
- metadata +176 -48
- data/lib/app/controllers/netzke_controller.rb +0 -46
- data/lib/app/models/netzke_preference.rb +0 -171
- data/lib/netzke/action_view_ext.rb +0 -81
- data/lib/netzke/base_js.rb +0 -339
- data/lib/netzke/routing.rb +0 -9
- data/test/app_root/app/models/role.rb +0 -3
- data/test/app_root/app/models/user.rb +0 -3
- data/test/app_root/config/boot.rb +0 -115
- data/test/app_root/config/database.yml +0 -31
- data/test/app_root/config/environment.rb +0 -14
- data/test/app_root/config/environments/sqlite.rb +0 -0
- data/test/app_root/config/environments/sqlite3.rb +0 -0
- data/test/app_root/config/routes.rb +0 -4
- data/test/app_root/db/migrate/20090423214303_create_roles.rb +0 -11
- data/test/app_root/db/migrate/20090423222114_create_users.rb +0 -12
- data/test/app_root/lib/console_with_fixtures.rb +0 -4
- data/test/app_root/script/console +0 -7
data/lib/netzke/base.rb
CHANGED
@@ -1,28 +1,36 @@
|
|
1
|
-
require '
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
require 'netzke/core_ext'
|
3
|
+
require 'netzke/javascript'
|
4
|
+
require 'netzke/stylesheets'
|
5
|
+
require 'netzke/services'
|
6
|
+
require 'netzke/composition'
|
7
|
+
require 'netzke/configuration'
|
8
|
+
require 'netzke/persistence'
|
9
|
+
require 'netzke/embedding'
|
10
|
+
require 'netzke/actions'
|
11
|
+
require 'netzke/session'
|
2
12
|
|
3
13
|
module Netzke
|
4
14
|
# = Base
|
5
|
-
# Base class for every Netzke
|
15
|
+
# Base class for every Netzke component
|
6
16
|
#
|
7
|
-
# To instantiate a
|
17
|
+
# To instantiate a component in the controller:
|
8
18
|
#
|
9
|
-
# netzke :
|
19
|
+
# netzke :component_name, configuration_hash
|
10
20
|
#
|
11
21
|
# == Configuration
|
12
|
-
# * <tt>:class_name</tt> - name of the
|
13
|
-
# When a
|
22
|
+
# * <tt>:class_name</tt> - name of the component class in the scope of the Netzke module, e.g. "FormPanel".
|
23
|
+
# When a component is defined in the controller and this option is omitted, component class is deferred from the component's
|
14
24
|
# name. E.g.:
|
15
25
|
#
|
16
26
|
# netzke :grid_panel, :model => "User"
|
17
27
|
#
|
18
28
|
# In this case <tt>:class_name</tt> is assumed to be "GridPanel"
|
19
29
|
#
|
20
|
-
# * <tt>:
|
21
|
-
# configuration that comes here will be available inside the javascript instance of the widget.
|
22
|
-
# * <tt>:persistent_config</tt> - if set to <tt>true</tt>, the widget will use persistent storage to store its state;
|
30
|
+
# * <tt>:persistent_config</tt> - if set to <tt>true</tt>, the component will use persistent storage to store its state;
|
23
31
|
# for instance, Netzke::GridPanel stores there its columns state (width, visibility, order, headers, etc).
|
24
|
-
# A
|
25
|
-
# are examples of
|
32
|
+
# A component may or may not provide interface to its persistent settings. GridPanel and FormPanel from netzke-basepack
|
33
|
+
# are examples of components that by default do.
|
26
34
|
#
|
27
35
|
# Examples of configuration:
|
28
36
|
#
|
@@ -30,565 +38,105 @@ module Netzke
|
|
30
38
|
# :class_name => "GridPanel",
|
31
39
|
# :model => "Book", # GridPanel specific option
|
32
40
|
# :persistent_config => false, # don't use persistent config for this instance
|
33
|
-
# :
|
34
|
-
#
|
35
|
-
# :title => "My books"
|
36
|
-
# }
|
41
|
+
# :icon_cls => 'icon-grid',
|
42
|
+
# :title => "My books"
|
37
43
|
#
|
38
44
|
# netzke :form_panel,
|
39
45
|
# :model => "User" # FormPanel specific option
|
40
46
|
class Base
|
41
|
-
extend ActiveSupport::Memoizable
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
attr_accessor :parent, :name, :global_id #, :permissions, :session
|
46
|
-
|
47
|
-
# Class-level Netzke::Base configuration. The defaults also get specified here.
|
48
|
-
def self.config
|
49
|
-
set_default_config({
|
50
|
-
# Set to true, if you want generated JS to be minified
|
51
|
-
:minify_js => Rails.env.production?,
|
52
|
-
# Which javascripts and stylesheets must get included at the initial load (see netzke-core.rb)
|
53
|
-
:javascripts => [],
|
54
|
-
:stylesheets => [],
|
55
|
-
|
56
|
-
:external_css => [],
|
57
|
-
|
58
|
-
# AR model that provides us with persistent config functionality
|
59
|
-
:persistent_config_manager => "NetzkePreference",
|
60
|
-
|
61
|
-
# Default location of extjs library
|
62
|
-
:ext_location => defined?(RAILS_ROOT) && "#{RAILS_ROOT}/public/extjs",
|
63
|
-
|
64
|
-
# Default location of icons, relative to the root of the domain
|
65
|
-
:icons_uri => ActionController::Base.relative_url_root.to_s+"/images/icons/" ,
|
66
|
-
|
67
|
-
# Default instance config
|
68
|
-
:default_config => {
|
69
|
-
:persistent_config => true
|
70
|
-
}
|
71
|
-
})
|
72
|
-
end
|
73
|
-
|
74
|
-
def self.set_default_config(c) #:nodoc:
|
75
|
-
@@config ||= {}
|
76
|
-
@@config[self.name] ||= c
|
77
|
-
end
|
78
|
-
|
79
|
-
# Override class-level defaults specified in <tt>Netzke::Base.config</tt>.
|
80
|
-
# E.g. in config/initializers/netzke-config.rb:
|
81
|
-
#
|
82
|
-
# Netzke::GridPanel.configure :default_config => {:persistent_config => true}
|
83
|
-
# Netzke::GridPanel.configure :column_filters_available, true
|
84
|
-
def self.configure(*args)
|
85
|
-
if args.first.is_a?(Symbol)
|
86
|
-
config[args.first] = args.last
|
87
|
-
else
|
88
|
-
# first arg is hash
|
89
|
-
config.deep_merge!(args.first)
|
90
|
-
end
|
91
|
-
# widget may implement some kind of control for configuration consistency
|
92
|
-
enforce_config_consistency if respond_to?(:enforce_config_consistency)
|
93
|
-
end
|
48
|
+
class_attribute :default_instance_config
|
49
|
+
self.default_instance_config = {}
|
94
50
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
51
|
+
include Session
|
52
|
+
include Persistence
|
53
|
+
include Configuration
|
54
|
+
include Javascript
|
55
|
+
include Services
|
56
|
+
include Composition
|
57
|
+
include Stylesheets
|
58
|
+
include Embedding
|
59
|
+
include Actions
|
105
60
|
|
106
|
-
|
107
|
-
@@session = s
|
108
|
-
end
|
109
|
-
|
110
|
-
# Should be called by session controller at the moment of successfull login
|
111
|
-
def self.login
|
112
|
-
session[:_netzke_next_request_is_first_after_login] = true
|
113
|
-
end
|
114
|
-
|
115
|
-
# Should be called by session controller at the moment of logout
|
116
|
-
def self.logout
|
117
|
-
session[:_netzke_next_request_is_first_after_logout] = true
|
118
|
-
end
|
119
|
-
|
120
|
-
# Declare connection points between client side of a widget and its server side. For example:
|
121
|
-
#
|
122
|
-
# api :reset_data
|
123
|
-
#
|
124
|
-
# will provide JavaScript side with a method <tt>resetData</tt> that will result in a call to Ruby
|
125
|
-
# method <tt>reset_data</tt>, e.g.:
|
126
|
-
#
|
127
|
-
# this.resetData({hard:true});
|
128
|
-
#
|
129
|
-
# See netzke-basepack's GridPanel for an example.
|
130
|
-
def self.api(*api_points)
|
131
|
-
apip = read_inheritable_attribute(:api_points) || []
|
132
|
-
api_points.each{|p| apip << p}
|
133
|
-
write_inheritable_attribute(:api_points, apip)
|
134
|
-
|
135
|
-
# It may be needed later for security
|
136
|
-
api_points.each do |apip|
|
137
|
-
module_eval <<-END, __FILE__, __LINE__
|
138
|
-
def api_#{apip}(*args)
|
139
|
-
before_api_call_result = defined?(before_api_call) && before_api_call('#{apip}', *args) || {}
|
140
|
-
(before_api_call_result.empty? ? #{apip}(*args) : before_api_call_result).to_nifty_json
|
141
|
-
end
|
142
|
-
# FIXME: commented out because otherwise ColumnOperations stop working
|
143
|
-
# def #{apip}(*args)
|
144
|
-
# flash :warning => "API point '#{apip}' is not implemented for widget '#{short_widget_class_name}'"
|
145
|
-
# {:flash => @flash}
|
146
|
-
# end
|
147
|
-
END
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
api :load_aggregatee_with_cache # every widget gets this api
|
152
|
-
|
153
|
-
# Array of API-points specified with <tt>Netzke::Base.api</tt> method
|
154
|
-
def self.api_points
|
155
|
-
read_inheritable_attribute(:api_points)
|
156
|
-
end
|
157
|
-
|
158
|
-
# Instance of widget by config
|
159
|
-
def self.instance_by_config(config)
|
160
|
-
::ActiveSupport::Deprecation.warn("widget_class_name option is deprecated. Use class_name instead", caller) if config[:widget_class_name]
|
161
|
-
widget_class = "Netzke::#{config[:class_name] || config[:class_name]}".constantize
|
162
|
-
widget_class.new(config)
|
163
|
-
end
|
164
|
-
|
165
|
-
# Persistent config manager class
|
166
|
-
def self.persistent_config_manager_class
|
167
|
-
Netzke::Base.config[:persistent_config_manager].try(:constantize)
|
168
|
-
rescue NameError
|
169
|
-
nil
|
170
|
-
end
|
171
|
-
|
172
|
-
# Example:
|
173
|
-
# masquarade_as(:role, 2)
|
174
|
-
# masquarade_as(:user, 4)
|
175
|
-
# masquarade_as(:world)
|
176
|
-
def self.masquerade_as(authority_level, authority_id = true)
|
177
|
-
reset_masquerading
|
178
|
-
session.merge!(:"masq_#{authority_level}" => authority_id)
|
179
|
-
end
|
180
|
-
|
181
|
-
def self.reset_masquerading
|
182
|
-
session[:masq_world] = session[:masq_role] = session[:masq_user] = nil
|
183
|
-
end
|
184
|
-
|
185
|
-
# Who are we acting as?
|
186
|
-
def self.authority_level
|
187
|
-
if session[:masq_world]
|
188
|
-
:world
|
189
|
-
elsif session[:masq_role]
|
190
|
-
[:role, session[:masq_role]]
|
191
|
-
elsif session[:masq_user]
|
192
|
-
[:user, session[:masq_user]]
|
193
|
-
elsif session[:netzke_user_id]
|
194
|
-
[:self, session[:netzke_user_id]]
|
195
|
-
else
|
196
|
-
:none # or nil ?
|
197
|
-
end
|
198
|
-
end
|
61
|
+
attr_accessor :parent, :name, :global_id #, :permissions, :session
|
199
62
|
|
200
|
-
#
|
201
|
-
# * the config hash is available to the
|
63
|
+
# Component initialization process
|
64
|
+
# * the config hash is available to the component after the "super" call in the initializer
|
202
65
|
# * override/add new default configuration options into the "default_config" method
|
203
66
|
# (the config hash is not yet available)
|
204
|
-
def initialize(
|
205
|
-
|
206
|
-
@passed_config
|
67
|
+
def initialize(conf = {}, parent = nil)
|
68
|
+
@passed_config = conf # configuration passed at the moment of instantiation
|
69
|
+
@passed_config.deep_freeze
|
207
70
|
@parent = parent
|
208
|
-
@name =
|
71
|
+
@name = conf[:name].nil? ? short_component_class_name.underscore : conf[:name].to_s
|
209
72
|
@global_id = parent.nil? ? @name : "#{parent.global_id}__#{@name}"
|
210
73
|
@flash = []
|
211
|
-
end
|
212
|
-
|
213
|
-
def session
|
214
|
-
Netzke::Base.session
|
215
|
-
end
|
216
|
-
|
217
|
-
#
|
218
|
-
# Configuration
|
219
|
-
#
|
220
|
-
|
221
|
-
# Default config - before applying any passed configuration
|
222
|
-
def default_config
|
223
|
-
self.class.config[:default_config].nil? ? {} : {}.merge(self.class.config[:default_config])
|
224
|
-
end
|
225
|
-
|
226
|
-
# Static, hardcoded config. Consists of default values merged with config that was passed during instantiation
|
227
|
-
def initial_config
|
228
|
-
default_config.deep_merge(@passed_config)
|
229
|
-
end
|
230
|
-
memoize :initial_config
|
231
|
-
|
232
|
-
# Config that is not overwritten by parents and sessions
|
233
|
-
def independent_config
|
234
|
-
initial_config.deep_merge(persistent_config_hash)
|
235
|
-
end
|
236
|
-
memoize :independent_config
|
237
|
-
|
238
|
-
# If the widget has persistent config in its disposal
|
239
|
-
def persistent_config_enabled?
|
240
|
-
!persistent_config_manager_class.nil? && initial_config[:persistent_config]
|
241
|
-
end
|
242
|
-
|
243
|
-
# Access to own persistent config, e.g.:
|
244
|
-
# persistent_config["window.size"] = 100
|
245
|
-
# persistent_config["window.size"] => 100
|
246
|
-
# This method is user/role-aware
|
247
|
-
def persistent_config
|
248
|
-
if persistent_config_enabled?
|
249
|
-
config_class = self.class.persistent_config_manager_class
|
250
|
-
config_class.widget_name = persistence_key.to_s # pass to the config class our unique name
|
251
|
-
config_class
|
252
|
-
else
|
253
|
-
# if we can't use presistent config, all the calls to it will always return nil,
|
254
|
-
# and the "="-operation will be ignored
|
255
|
-
logger.debug "==> NETZKE: no persistent config is set up for widget '#{global_id}'"
|
256
|
-
{}
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
# Access to the global persistent config (e.g. of another widget)
|
261
|
-
def global_persistent_config(owner = nil)
|
262
|
-
config_class = self.class.persistent_config_manager_class
|
263
|
-
config_class.widget_name = owner
|
264
|
-
config_class
|
265
|
-
end
|
266
|
-
|
267
|
-
# A string which will identify NetzkePreference records for this widget.
|
268
|
-
# If <tt>persistence_key</tt> is passed, use it. Otherwise use global widget's id.
|
269
|
-
def persistence_key #:nodoc:
|
270
|
-
# initial_config[:persistence_key] ? parent.try(:persistence_key) ? "#{parent.persistence_key}__#{initial_config[:persistence_key]}".to_sym : initial_config[:persistence_key] : global_id.to_sym
|
271
|
-
initial_config[:persistence_key] ? initial_config[:persistence_key] : global_id.to_sym
|
272
|
-
end
|
273
|
-
|
274
|
-
# Update persistent_config[:ext_config] hash
|
275
|
-
def update_persistent_ext_config(hsh)
|
276
|
-
current_config = persistent_config[:ext_config] || {}
|
277
|
-
current_config.deep_merge!(hsh.deep_convert_keys{ |k| k.to_s }) # first, recursively stringify the keys
|
278
|
-
persistent_config[:ext_config] = current_config
|
279
|
-
end
|
280
|
-
|
281
|
-
# Resulting config that takes into account all possible ways to configure a widget. *Read only*.
|
282
|
-
# Translates into something like this:
|
283
|
-
# default_config.
|
284
|
-
# deep_merge(@passed_config).
|
285
|
-
# deep_merge(persistent_config_hash).
|
286
|
-
# deep_merge(strong_parent_config).
|
287
|
-
# deep_merge(strong_session_config)
|
288
|
-
def config
|
289
|
-
independent_config.deep_merge(strong_parent_config).deep_merge(strong_session_config)
|
290
|
-
end
|
291
|
-
memoize :config
|
292
|
-
|
293
|
-
def flat_config(key = nil)
|
294
|
-
fc = config.flatten_with_type
|
295
|
-
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
296
|
-
end
|
297
|
-
|
298
|
-
def strong_parent_config
|
299
|
-
@strong_parent_config ||= parent.nil? ? {} : parent.strong_children_config
|
300
|
-
end
|
301
|
-
|
302
|
-
def flat_independent_config(key = nil)
|
303
|
-
fc = independent_config.flatten_with_type
|
304
|
-
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
305
|
-
end
|
306
|
-
|
307
|
-
def flat_default_config(key = nil)
|
308
|
-
fc = default_config.flatten_with_type
|
309
|
-
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
310
|
-
end
|
311
|
-
|
312
|
-
def flat_initial_config(key = nil)
|
313
|
-
fc = initial_config.flatten_with_type
|
314
|
-
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
315
|
-
end
|
316
|
-
|
317
|
-
# Returns a hash built from all persistent config values for the current widget, following the double underscore
|
318
|
-
# naming convention. E.g., if we have the following persistent config pairs:
|
319
|
-
# enabled => true
|
320
|
-
# layout__width => 100
|
321
|
-
# layout__header__height => 20
|
322
|
-
#
|
323
|
-
# this method will return the following hash:
|
324
|
-
# {:enabled => true, :layout => {:width => 100, :header => {:height => 20}}}
|
325
|
-
def persistent_config_hash
|
326
|
-
return {} if !initial_config[:persistent_config] || Netzke::Base.persistent_config_manager_class.nil?
|
327
74
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
tmp_res = tmp_res[level_prefix]
|
338
|
-
end
|
339
|
-
# Now 'anchor' is a hash that represents the path to the single value,
|
340
|
-
# for example: {:ext_config => {:title => 100}} (which corresponds to ext_config__title)
|
341
|
-
# So we need to recursively merge it into the final result
|
342
|
-
res.deep_merge!(hsh_levels.first => anchor)
|
343
|
-
end
|
344
|
-
res.deep_convert_keys{ |k| k.to_sym } # recursively symbolize the keys
|
345
|
-
end
|
346
|
-
memoize :persistent_config_hash
|
347
|
-
|
348
|
-
# Helper to access config[:ext_config]
|
349
|
-
def ext_config
|
350
|
-
config[:ext_config] || {}
|
351
|
-
end
|
352
|
-
|
353
|
-
# Like normal config, but stored in session
|
354
|
-
def weak_session_config
|
355
|
-
widget_session[:weak_session_config] ||= {}
|
356
|
-
end
|
357
|
-
|
358
|
-
def strong_session_config
|
359
|
-
widget_session[:strong_session_config] ||= {}
|
75
|
+
# initialize @components and @items
|
76
|
+
normalize_components_in_items
|
77
|
+
# auto_collect_actions_from_config_and_js_properties
|
78
|
+
end
|
79
|
+
|
80
|
+
# Short component class name, e.g.:
|
81
|
+
# Netzke::Module::SomeComponent => Module::SomeComponent
|
82
|
+
def self.short_component_class_name
|
83
|
+
self.name.sub(/^Netzke::/, "")
|
360
84
|
end
|
361
85
|
|
362
|
-
#
|
363
|
-
|
364
|
-
|
365
|
-
# end
|
366
|
-
|
367
|
-
# This config will be picked up by all the descendants
|
368
|
-
def strong_children_config
|
369
|
-
@strong_children_config ||= parent.nil? ? {} : parent.strong_children_config
|
86
|
+
# Component class, given its name
|
87
|
+
def self.constantize_class_name(class_name)
|
88
|
+
"#{class_name}".constantize rescue "Netzke::#{class_name}".constantize
|
370
89
|
end
|
371
90
|
|
372
|
-
|
373
|
-
|
374
|
-
# @weak_children_config = c
|
375
|
-
# end
|
376
|
-
|
377
|
-
def weak_children_config
|
378
|
-
@weak_children_config ||= {}
|
379
|
-
end
|
380
|
-
|
381
|
-
def widget_session
|
382
|
-
session[global_id] ||= {}
|
91
|
+
def constantize_class_name(class_name)
|
92
|
+
self.class.constantize_class_name(class_name)
|
383
93
|
end
|
384
94
|
|
385
|
-
#
|
386
|
-
def
|
387
|
-
|
95
|
+
# Instance of component by config
|
96
|
+
def self.instance_by_config(config)
|
97
|
+
constantize_class_name(config[:class_name]).new(config)
|
388
98
|
end
|
389
|
-
|
390
|
-
def
|
391
|
-
|
392
|
-
|
393
|
-
|
99
|
+
|
100
|
+
def logger
|
101
|
+
if defined?(Rails)
|
102
|
+
Rails.logger
|
103
|
+
else
|
104
|
+
require 'logger'
|
105
|
+
Logger.new(STDOUT)
|
394
106
|
end
|
395
|
-
res << short_widget_class_name
|
396
|
-
res.uniq
|
397
107
|
end
|
398
|
-
|
108
|
+
|
399
109
|
# 'Netzke::Grid' => 'Grid'
|
400
|
-
def
|
401
|
-
self.class.
|
402
|
-
end
|
403
|
-
|
404
|
-
## Dependencies
|
405
|
-
def dependencies
|
406
|
-
@dependencies ||= begin
|
407
|
-
non_late_aggregatees_widget_classes = non_late_aggregatees.values.map{|v| v[:class_name]}
|
408
|
-
(initial_dependencies + non_late_aggregatees_widget_classes << self.class.short_widget_class_name).uniq
|
409
|
-
end
|
410
|
-
end
|
411
|
-
|
412
|
-
# override this method if you need some extra dependencies, which are not the aggregatees
|
413
|
-
def initial_dependencies
|
414
|
-
[]
|
415
|
-
end
|
416
|
-
|
417
|
-
### Aggregation
|
418
|
-
def initial_aggregatees
|
419
|
-
{}
|
420
|
-
end
|
421
|
-
|
422
|
-
def aggregatees
|
423
|
-
@aggregatees ||= initial_aggregatees.merge(initial_late_aggregatees.each_pair{|k,v| v.merge!(:late_aggregation => true)})
|
424
|
-
end
|
425
|
-
|
426
|
-
def non_late_aggregatees
|
427
|
-
aggregatees.reject{|k,v| v[:late_aggregation]}
|
428
|
-
end
|
429
|
-
|
430
|
-
def add_aggregatee(aggr)
|
431
|
-
aggregatees.merge!(aggr)
|
110
|
+
def short_component_class_name
|
111
|
+
self.class.short_component_class_name
|
432
112
|
end
|
433
|
-
|
434
|
-
def remove_aggregatee(aggr)
|
435
|
-
if config[:persistent_config]
|
436
|
-
persistent_config_manager_class.delete_all_for_widget("#{global_id}__#{aggr}")
|
437
|
-
end
|
438
|
-
aggregatees[aggr] = nil
|
439
|
-
end
|
440
|
-
|
441
|
-
# The difference between aggregatees and late aggregatees is the following: the former gets instantiated together with its aggregator and is normally *instantly* visible as a part of it (for example, the widget in the initially expanded panel in an Accordion). A late aggregatee doesn't get instantiated along with its aggregator. Until it gets requested from the server, it doesn't take any part in its aggregator's life. An example of late aggregatee could be a widget that is loaded dynamically into a previously collapsed panel of an Accordion, or a preferences window (late aggregatee) for a widget (aggregator) that only gets shown when user wants to edit widget's preferences.
|
442
|
-
def initial_late_aggregatees
|
443
|
-
{}
|
444
|
-
end
|
445
|
-
|
446
|
-
def add_late_aggregatee(aggr)
|
447
|
-
aggregatees.merge!(aggr.merge(:late_aggregation => true))
|
448
|
-
end
|
449
|
-
|
450
|
-
# recursively instantiates an aggregatee based on its "path": e.g. if we have an aggregatee :aggr1 which in its turn has an aggregatee :aggr10, the path to the latter would be "aggr1__aggr10"
|
451
|
-
# TODO: introduce memoization
|
452
|
-
def aggregatee_instance(name, strong_config = {})
|
453
|
-
aggregator = self
|
454
|
-
name.to_s.split('__').each do |aggr|
|
455
|
-
aggr = aggr.to_sym
|
456
|
-
aggregatee_config = aggregator.aggregatees[aggr]
|
457
|
-
raise ArgumentError, "No aggregatee '#{aggr}' defined for widget '#{aggregator.global_id}'" if aggregatee_config.nil?
|
458
|
-
::ActiveSupport::Deprecation.warn("widget_class_name option is deprecated. Use class_name instead", caller) if aggregatee_config[:widget_class_name]
|
459
|
-
short_widget_class_name = aggregatee_config[:class_name] || aggregatee_config[:widget_class_name]
|
460
|
-
raise ArgumentError, "No class_name specified for aggregatee #{aggr} of #{aggregator.global_id}" if short_widget_class_name.nil?
|
461
|
-
widget_class = "Netzke::#{short_widget_class_name}".constantize
|
462
|
-
|
463
|
-
conf = weak_children_config.
|
464
|
-
deep_merge(aggregatee_config).
|
465
|
-
deep_merge(strong_config). # we may want to reconfigure the aggregatee at the moment of instantiation
|
466
|
-
merge(:name => aggr)
|
467
|
-
|
468
|
-
aggregator = widget_class.new(conf, aggregator) # params: config, parent
|
469
|
-
# aggregator.weak_children_config = weak_children_config
|
470
|
-
# aggregator.strong_children_config = strong_children_config
|
471
|
-
end
|
472
|
-
aggregator
|
473
|
-
end
|
474
|
-
|
475
|
-
def full_class_name(short_name)
|
476
|
-
"Netzke::#{short_name}"
|
477
|
-
end
|
478
|
-
|
113
|
+
|
479
114
|
def flash(flash_hash)
|
480
115
|
level = flash_hash.keys.first
|
481
116
|
raise "Unknown message level for flash" unless %(notice warning error).include?(level.to_s)
|
482
117
|
@flash << {:level => level, :msg => flash_hash[level]}
|
483
118
|
end
|
484
119
|
|
485
|
-
def
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
# called when the method_missing tries to processes a non-existing aggregatee
|
490
|
-
def aggregatee_missing(aggr)
|
491
|
-
flash :error => "Unknown aggregatee #{aggr} for widget #{name}"
|
492
|
-
{:feedback => @flash}.to_nifty_json
|
493
|
-
end
|
494
|
-
|
495
|
-
def tools
|
496
|
-
persistent_config[:tools] ||= config[:tools] || []
|
497
|
-
end
|
498
|
-
|
499
|
-
def menu
|
500
|
-
persistent_config[:menu] ||= config[:menu] == false ? nil : config[:menu]
|
120
|
+
def self.read_clean_inheritable_hash(attr_name)
|
121
|
+
res = read_inheritable_attribute(attr_name) || {}
|
122
|
+
# We don't want here any values from the superclass (which is the consequence of using inheritable attributes).
|
123
|
+
res == self.superclass.read_inheritable_attribute(attr_name) ? {} : res
|
501
124
|
end
|
502
125
|
|
503
|
-
# some convenience for instances
|
504
|
-
def persistent_config_manager_class
|
505
|
-
self.class.persistent_config_manager_class
|
506
|
-
end
|
507
|
-
|
508
126
|
# override this method to do stuff at the moment of loading by some parent
|
509
127
|
def before_load
|
510
|
-
|
128
|
+
# component_session.clear - we don't do it anymore
|
511
129
|
end
|
512
130
|
|
513
|
-
# Returns global id of a widget in the hierarchy, based on passed reference that follows
|
514
|
-
# the double-underscore notation. Referring to "parent" is allowed. If going to far up the hierarchy will
|
515
|
-
# result in <tt>nil</tt>, while referring to a non-existent aggregatee will simply provide an erroneous ID.
|
516
|
-
# Example:
|
517
|
-
# <tt>parent__parent__child__subchild</tt> will traverse the hierarchy 2 levels up, then going down to "child",
|
518
|
-
# and further to "subchild". If such a widget exists in the hierarchy, its global id will be returned, otherwise
|
519
|
-
# <tt>nil</tt> will be returned.
|
520
|
-
def global_id_by_reference(ref)
|
521
|
-
ref = ref.to_s
|
522
|
-
return parent && parent.global_id if ref == "parent"
|
523
|
-
substr = ref.sub(/^parent__/, "")
|
524
|
-
if substr == ref # there's no "parent__" in the beginning
|
525
|
-
return global_id + "__" + ref
|
526
|
-
else
|
527
|
-
return parent.global_id_by_reference(substr)
|
528
|
-
end
|
529
|
-
end
|
530
131
|
|
531
|
-
#
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
# * <tt>:container</tt> - Ext id of the container where in which the aggregatee will be rendered
|
536
|
-
def load_aggregatee_with_cache(params)
|
537
|
-
cache = params[:cache].gsub(".", "::").split(",") # array of cached class names (in Ruby)
|
538
|
-
relative_widget_id = params.delete(:id).underscore.to_sym
|
539
|
-
widget = aggregatees[relative_widget_id] && aggregatee_instance(relative_widget_id)
|
540
|
-
|
541
|
-
if widget
|
542
|
-
# inform the widget that it's being loaded
|
543
|
-
widget.before_load
|
544
|
-
|
545
|
-
[{
|
546
|
-
:js => widget.js_missing_code(cache),
|
547
|
-
:css => widget.css_missing_code(cache)
|
548
|
-
}, {
|
549
|
-
:render_widget_in_container => { # TODO: rename it
|
550
|
-
:container => params[:container],
|
551
|
-
:config => widget.js_config
|
552
|
-
}
|
553
|
-
}, {
|
554
|
-
:widget_loaded => {
|
555
|
-
:id => relative_widget_id
|
556
|
-
}
|
557
|
-
}]
|
558
|
-
else
|
559
|
-
{:feedback => "Couldn't load aggregatee '#{relative_widget_id}'"}
|
560
|
-
end
|
561
|
-
end
|
562
|
-
|
563
|
-
# Method dispatcher - instantiates an aggregatee and calls the method on it
|
564
|
-
# E.g.:
|
565
|
-
# users__center__get_data
|
566
|
-
# instantiates aggregatee "users", and calls "center__get_data" on it
|
567
|
-
# books__move_column
|
568
|
-
# instantiates aggregatee "books", and calls "api_move_column" on it
|
569
|
-
def method_missing(method_name, params = {})
|
570
|
-
widget, *action = method_name.to_s.split('__')
|
571
|
-
widget = widget.to_sym
|
572
|
-
action = !action.empty? && action.join("__").to_sym
|
573
|
-
|
574
|
-
if action
|
575
|
-
if aggregatees[widget]
|
576
|
-
# only actions starting with "api_" are accessible
|
577
|
-
api_action = action.to_s.index('__') ? action : "api_#{action}"
|
578
|
-
aggregatee_instance(widget).send(api_action, params)
|
579
|
-
else
|
580
|
-
aggregatee_missing(widget)
|
581
|
-
end
|
132
|
+
# Dependencies
|
133
|
+
def self.class_ancestors
|
134
|
+
if self == Netzke::Base
|
135
|
+
[]
|
582
136
|
else
|
583
|
-
|
137
|
+
superclass.class_ancestors + [self]
|
584
138
|
end
|
585
139
|
end
|
586
140
|
|
587
|
-
# Register the configuration for the widget in the session, and also remember that the code for it has been rendered
|
588
|
-
def self.reg_widget(config)
|
589
|
-
session[:netzke_widgets] ||= {}
|
590
|
-
session[:netzke_widgets][config[:name]] = config
|
591
|
-
end
|
592
|
-
|
593
141
|
end
|
594
142
|
end
|