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
@@ -0,0 +1,251 @@
|
|
1
|
+
# require 'active_support/core_ext/class/inheritable_attributes'
|
2
|
+
|
3
|
+
module Netzke
|
4
|
+
module Composition
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
|
9
|
+
# Loads a component on browser's request. Every Nettzke component gets this endpoint.
|
10
|
+
# <tt>params</tt> should contain:
|
11
|
+
# * <tt>:cache</tt> - an array of component classes cached at the browser
|
12
|
+
# * <tt>:id</tt> - reference to the component
|
13
|
+
# * <tt>:container</tt> - Ext id of the container where in which the component will be rendered
|
14
|
+
endpoint :deliver_component do |params|
|
15
|
+
cache = params[:cache].gsub(".", "::").split(",") # array of cached class names (in Ruby)
|
16
|
+
component_name = params.delete(:name).underscore.to_sym
|
17
|
+
component = components[component_name] && component_instance(component_name)
|
18
|
+
|
19
|
+
if component
|
20
|
+
# inform the component that it's being loaded
|
21
|
+
component.before_load
|
22
|
+
|
23
|
+
[{
|
24
|
+
:eval_js => component.js_missing_code(cache),
|
25
|
+
:eval_css => component.css_missing_code(cache)
|
26
|
+
}, {
|
27
|
+
:component_delivered => component.js_config
|
28
|
+
}]
|
29
|
+
else
|
30
|
+
{:feedback => "Couldn't load component '#{component_name}'"}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end # included
|
35
|
+
|
36
|
+
module ClassMethods
|
37
|
+
|
38
|
+
# Defines a nested component.
|
39
|
+
# For example:
|
40
|
+
#
|
41
|
+
# component :users, :data_class => "GridPanel", :model => "User"
|
42
|
+
#
|
43
|
+
# It can also accept a block (receiving as parameter the eventual definition from super class):
|
44
|
+
#
|
45
|
+
# component :books do |orig|
|
46
|
+
# {:data_class => "Book", :title => orig[:title] + ", extended"}
|
47
|
+
# end
|
48
|
+
def component(name, config = {}, &block)
|
49
|
+
config = config.dup
|
50
|
+
config[:class_name] ||= name.to_s.camelize
|
51
|
+
config[:name] = name.to_s
|
52
|
+
method_name = "_#{name}_component"
|
53
|
+
|
54
|
+
if block_given?
|
55
|
+
define_method(method_name, &block)
|
56
|
+
else
|
57
|
+
define_method(method_name) do
|
58
|
+
config
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Component's js config used when embedding components as Container's items
|
64
|
+
# (see some_composite.rb for an example)
|
65
|
+
def js_component(name, config = {})
|
66
|
+
config.merge(:component => name)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
module InstanceMethods
|
72
|
+
|
73
|
+
def items
|
74
|
+
@items_with_normalized_components
|
75
|
+
end
|
76
|
+
|
77
|
+
def initial_components
|
78
|
+
{}
|
79
|
+
end
|
80
|
+
|
81
|
+
# All components for this instance, which includes components defined on class level, and components detected in :items
|
82
|
+
def components
|
83
|
+
# items if @components.nil? # simply trigger collecting @components from items
|
84
|
+
# self.class.components.merge(@components || {})
|
85
|
+
|
86
|
+
@components ||= begin
|
87
|
+
method_regexp = /^_(.+)_component$/
|
88
|
+
self.class.instance_methods.grep(method_regexp).inject({}) do |r, m|
|
89
|
+
m.match(method_regexp)
|
90
|
+
r.merge($1.to_sym => send(m))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def non_late_components
|
96
|
+
components.reject{|k,v| v[:lazy_loading]}
|
97
|
+
end
|
98
|
+
|
99
|
+
def add_component(aggr)
|
100
|
+
components.merge!(aggr)
|
101
|
+
end
|
102
|
+
|
103
|
+
def remove_component(aggr)
|
104
|
+
if config[:persistent_config]
|
105
|
+
persistence_manager_class.delete_all_for_component("#{global_id}__#{aggr}")
|
106
|
+
end
|
107
|
+
components[aggr] = nil
|
108
|
+
end
|
109
|
+
|
110
|
+
# The difference between components and late components is the following: the former gets instantiated together with its composite and is normally *instantly* visible as a part of it (for example, the component in the initially expanded panel in an Accordion). A late component doesn't get instantiated along with its composite. Until it gets requested from the server, it doesn't take any part in its composite's life. An example of late component could be a component that is loaded dynamically into a previously collapsed panel of an Accordion, or a preferences window (late component) for a component (composite) that only gets shown when user wants to edit component's preferences.
|
111
|
+
def initial_late_components
|
112
|
+
{}
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_late_component(aggr)
|
116
|
+
components.merge!(aggr.merge(:lazy_loading => true))
|
117
|
+
end
|
118
|
+
|
119
|
+
# called when the method_missing tries to processes a non-existing component
|
120
|
+
def component_missing(aggr)
|
121
|
+
flash :error => "Unknown component #{aggr} for component #{name}"
|
122
|
+
{:feedback => @flash}.to_nifty_json
|
123
|
+
end
|
124
|
+
|
125
|
+
# recursively instantiates an component based on its "path": e.g. if we have an component :aggr1 which in its turn has an component :aggr10, the path to the latter would be "aggr1__aggr10"
|
126
|
+
def component_instance(name, strong_config = {})
|
127
|
+
@cached_component_instances ||= {}
|
128
|
+
@cached_component_instances[name] ||= begin
|
129
|
+
composite = self
|
130
|
+
name.to_s.split('__').each do |aggr|
|
131
|
+
aggr = aggr.to_sym
|
132
|
+
component_config = composite.components[aggr]
|
133
|
+
raise ArgumentError, "No component '#{aggr}' defined for component '#{composite.global_id}'" if component_config.nil?
|
134
|
+
short_component_class_name = component_config[:class_name]
|
135
|
+
raise ArgumentError, "No class_name specified for component #{aggr} of #{composite.global_id}" if short_component_class_name.nil?
|
136
|
+
component_class = constantize_class_name(short_component_class_name)
|
137
|
+
|
138
|
+
conf = weak_children_config.
|
139
|
+
deep_merge(component_config).
|
140
|
+
deep_merge(strong_config). # we may want to reconfigure the component at the moment of instantiation
|
141
|
+
merge(:name => aggr)
|
142
|
+
|
143
|
+
composite = component_class.new(conf, composite) # params: config, parent
|
144
|
+
# composite.weak_children_config = weak_children_config
|
145
|
+
# composite.strong_children_config = strong_children_config
|
146
|
+
end
|
147
|
+
composite
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def dependency_classes
|
152
|
+
res = []
|
153
|
+
|
154
|
+
non_late_components.keys.each do |aggr|
|
155
|
+
res += component_instance(aggr).dependency_classes
|
156
|
+
end
|
157
|
+
|
158
|
+
res += self.class.class_ancestors
|
159
|
+
|
160
|
+
res << self.class
|
161
|
+
res.uniq
|
162
|
+
end
|
163
|
+
|
164
|
+
## Dependencies
|
165
|
+
# def dependencies
|
166
|
+
# @dependencies ||= begin
|
167
|
+
# non_late_components_component_classes = non_late_components.values.map{|v| v[:class_name]}
|
168
|
+
# (initial_dependencies + non_late_components_component_classes << self.class.short_component_class_name).uniq
|
169
|
+
# end
|
170
|
+
# end
|
171
|
+
|
172
|
+
# override this method if you need some extra dependencies, which are not the components
|
173
|
+
def initial_dependencies
|
174
|
+
[]
|
175
|
+
end
|
176
|
+
|
177
|
+
def js_component(*args)
|
178
|
+
self.class.js_component(*args)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns global id of a component in the hierarchy, based on passed reference that follows
|
182
|
+
# the double-underscore notation. Referring to "parent" is allowed. If going to far up the hierarchy will
|
183
|
+
# result in <tt>nil</tt>, while referring to a non-existent component will simply provide an erroneous ID.
|
184
|
+
# Example:
|
185
|
+
# <tt>parent__parent__child__subchild</tt> will traverse the hierarchy 2 levels up, then going down to "child",
|
186
|
+
# and further to "subchild". If such a component exists in the hierarchy, its global id will be returned, otherwise
|
187
|
+
# <tt>nil</tt> will be returned.
|
188
|
+
def global_id_by_reference(ref)
|
189
|
+
ref = ref.to_s
|
190
|
+
return parent && parent.global_id if ref == "parent"
|
191
|
+
substr = ref.sub(/^parent__/, "")
|
192
|
+
if substr == ref # there's no "parent__" in the beginning
|
193
|
+
return global_id + "__" + ref
|
194
|
+
else
|
195
|
+
return parent.global_id_by_reference(substr)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Method dispatcher - instantiates an component and calls the method on it
|
200
|
+
# E.g.:
|
201
|
+
# users__center__get_data
|
202
|
+
# instantiates component "users", and calls "center__get_data" on it
|
203
|
+
# books__move_column
|
204
|
+
# instantiates component "books", and calls "endpoint_move_column" on it
|
205
|
+
def method_missing(method_name, params = {})
|
206
|
+
component, *action = method_name.to_s.split('__')
|
207
|
+
component = component.to_sym
|
208
|
+
action = !action.empty? && action.join("__").to_sym
|
209
|
+
|
210
|
+
if action
|
211
|
+
if components[component]
|
212
|
+
# only actions starting with "endpoint_" are accessible
|
213
|
+
endpoint_action = action.to_s.index('__') ? action : "endpoint_#{action}"
|
214
|
+
component_instance(component).send(endpoint_action, params)
|
215
|
+
else
|
216
|
+
component_missing(component)
|
217
|
+
end
|
218
|
+
else
|
219
|
+
super
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
def normalize_components(items)
|
226
|
+
@component_index ||= 0
|
227
|
+
@items_with_normalized_components = items.each_with_index.map do |item, i|
|
228
|
+
if is_component_config?(item)
|
229
|
+
component_name = item[:name] || :"#{item[:class_name].underscore.split("/").last}#{@component_index}"
|
230
|
+
@component_index += 1
|
231
|
+
self.class.component(component_name.to_sym, item)
|
232
|
+
js_component(component_name) # replace current item with a reference to component
|
233
|
+
elsif item.is_a?(Hash)
|
234
|
+
item[:items].is_a?(Array) ? item.merge(:items => normalize_components(item[:items])) : item
|
235
|
+
else
|
236
|
+
item
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def normalize_components_in_items
|
242
|
+
normalize_components(config[:items]) if config[:items]
|
243
|
+
end
|
244
|
+
|
245
|
+
def is_component_config?(c)
|
246
|
+
!!(c.is_a?(Hash) && c[:class_name])
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Netzke
|
2
|
+
module Configuration
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
CONFIGURATION_LEVELS = [:default, :initial, :independent, :session, :final]
|
6
|
+
|
7
|
+
included do
|
8
|
+
CONFIGURATION_LEVELS.each do |level|
|
9
|
+
define_method("weak_#{level}_options"){ {} }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def setup
|
15
|
+
yield self
|
16
|
+
end
|
17
|
+
|
18
|
+
# Config options that should not go to the client side
|
19
|
+
def server_side_config_options
|
20
|
+
[:lazy_loading, :class_name]
|
21
|
+
end
|
22
|
+
|
23
|
+
def config(*args, &block)
|
24
|
+
level = args.first.is_a?(Symbol) ? args.first : :final
|
25
|
+
config_hash = args.last.is_a?(Hash) && args.last
|
26
|
+
raise ArgumentError, "Config hash or block required" if !block_given? && !config_hash
|
27
|
+
if block_given?
|
28
|
+
define_method(:"weak_#{level}_options", &block)
|
29
|
+
else
|
30
|
+
define_method(:"weak_#{level}_options") do
|
31
|
+
config_hash
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
module InstanceMethods
|
39
|
+
# Default config - before applying any passed configuration
|
40
|
+
def default_config
|
41
|
+
@default_config ||= {}.merge(weak_default_options).merge(self.class.default_instance_config)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Static, hardcoded config. Consists of default values merged with config that was passed during instantiation
|
45
|
+
def initial_config
|
46
|
+
@initial_config ||= default_config.merge(weak_initial_options).merge(@passed_config)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Config that is not overridden by parents and sessions
|
50
|
+
def independent_config
|
51
|
+
@independent_config ||= initial_config.merge(weak_independent_options).merge(persistent_options)
|
52
|
+
end
|
53
|
+
|
54
|
+
def session_config
|
55
|
+
@session_config ||= independent_config.merge(weak_session_options).merge(session_options)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Last level config, overridden only by ineritance
|
59
|
+
def final_config
|
60
|
+
@strong_config ||= session_config.merge(weak_final_options).merge(strong_parent_config)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Resulting config that takes into account all possible ways to configure a component. *Read only*.
|
64
|
+
# Translates into something like this:
|
65
|
+
#
|
66
|
+
# default_config.
|
67
|
+
# deep_merge(@passed_config).
|
68
|
+
# deep_merge(persistent_options).
|
69
|
+
# deep_merge(strong_parent_config).
|
70
|
+
# deep_merge(strong_session_config)
|
71
|
+
#
|
72
|
+
# Moved out to a separate method in order to provide for easy caching.
|
73
|
+
# *Do not override this method*, use +Base.config+ instead.
|
74
|
+
def config
|
75
|
+
@config ||= final_config
|
76
|
+
end
|
77
|
+
|
78
|
+
def flat_config(key = nil)
|
79
|
+
fc = config.flatten_with_type
|
80
|
+
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
81
|
+
end
|
82
|
+
|
83
|
+
def strong_parent_config
|
84
|
+
@strong_parent_config ||= parent.nil? ? {} : parent.strong_children_config
|
85
|
+
end
|
86
|
+
|
87
|
+
def flat_independent_config(key = nil)
|
88
|
+
fc = independent_config.flatten_with_type
|
89
|
+
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
90
|
+
end
|
91
|
+
|
92
|
+
def flat_default_config(key = nil)
|
93
|
+
fc = default_config.flatten_with_type
|
94
|
+
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
95
|
+
end
|
96
|
+
|
97
|
+
def flat_initial_config(key = nil)
|
98
|
+
fc = initial_config.flatten_with_type
|
99
|
+
key.nil? ? fc : fc.select{ |c| c[:name] == key.to_sym }.first.try(:value)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Like normal config, but stored in session
|
103
|
+
# def weak_session_config
|
104
|
+
# component_session[:weak_session_config] ||= {}
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# def strong_session_config
|
108
|
+
# component_session[:strong_session_config] ||= {}
|
109
|
+
# end
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
# configuration of all children will get deep_merge'd with strong_children_config
|
114
|
+
# def strong_children_config= (c)
|
115
|
+
# @strong_children_config = c
|
116
|
+
# end
|
117
|
+
|
118
|
+
# This config will be picked up by all the descendants
|
119
|
+
def strong_children_config
|
120
|
+
@strong_children_config ||= parent.nil? ? {} : parent.strong_children_config
|
121
|
+
end
|
122
|
+
|
123
|
+
# configuration of all children will get reverse_deep_merge'd with weak_children_config
|
124
|
+
# def weak_children_config= (c)
|
125
|
+
# @weak_children_config = c
|
126
|
+
# end
|
127
|
+
|
128
|
+
def weak_children_config
|
129
|
+
@weak_children_config ||= {}
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Netzke
|
2
|
+
module Core
|
3
|
+
module Masquerading
|
4
|
+
# Example:
|
5
|
+
# masquarade_as(:role, 2)
|
6
|
+
# masquarade_as(:user, 4)
|
7
|
+
# masquarade_as(:world)
|
8
|
+
def masquerade_as(authority_level, authority_id = true)
|
9
|
+
reset_masquerading
|
10
|
+
session.merge!(:"masq_#{authority_level}" => authority_id)
|
11
|
+
end
|
12
|
+
|
13
|
+
def reset_masquerading
|
14
|
+
session[:masq_world] = session[:masq_role] = session[:masq_user] = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
# Who are we acting as?
|
18
|
+
def authority_level
|
19
|
+
if session[:masq_world]
|
20
|
+
:world
|
21
|
+
elsif session[:masq_role]
|
22
|
+
[:role, session[:masq_role]]
|
23
|
+
elsif session[:masq_user]
|
24
|
+
[:user, session[:masq_user]]
|
25
|
+
elsif session[:netzke_user_id]
|
26
|
+
[:self, session[:netzke_user_id]]
|
27
|
+
else
|
28
|
+
:none # or nil ?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Netzke
|
2
|
+
module Core
|
3
|
+
module Session
|
4
|
+
# Access to controller sessions
|
5
|
+
def session
|
6
|
+
@@session ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def session=(s)
|
10
|
+
@@session = s
|
11
|
+
end
|
12
|
+
|
13
|
+
# Should be called by session controller at the moment of successfull login
|
14
|
+
def login
|
15
|
+
session[:_netzke_next_request_is_first_after_login] = true
|
16
|
+
end
|
17
|
+
|
18
|
+
# Should be called by session controller at the moment of logout
|
19
|
+
def logout
|
20
|
+
session[:_netzke_next_request_is_first_after_logout] = true
|
21
|
+
end
|
22
|
+
|
23
|
+
# Register the configuration for the component in the session, and also remember that the code for it has been rendered
|
24
|
+
def reg_component(config)
|
25
|
+
session[:netzke_components] ||= {}
|
26
|
+
session[:netzke_components][config[:name]] = config
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/netzke/core.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
require 'netzke/core/version'
|
3
|
+
require 'netzke/core/session'
|
4
|
+
require 'netzke/core/masquerading'
|
5
|
+
|
6
|
+
module Netzke
|
7
|
+
# You can configure Netzke::Core like this:
|
8
|
+
#
|
9
|
+
# Netzke::Core.setup do |config|
|
10
|
+
# config.ext_location = "/home/netzke/ext-3.3.0"
|
11
|
+
# config.icons_uri = "/images/famfamfam/icons"
|
12
|
+
# # ...
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# The following configuration options are available:
|
16
|
+
# * ext_location - absolute path to your Ext code root
|
17
|
+
# * icons_uri - relative URI to the icons
|
18
|
+
# * javascript_on_main_page (true/false, defaults to false) - if you want the JS classes to be inserted into the code of the page,
|
19
|
+
# rather than into netzke.js (setting to true can be handy for debugging)
|
20
|
+
module Core
|
21
|
+
extend Session
|
22
|
+
extend Masquerading
|
23
|
+
|
24
|
+
mattr_accessor :controller
|
25
|
+
|
26
|
+
mattr_accessor :javascripts
|
27
|
+
@@javascripts = ["#{File.dirname(__FILE__)}/../../javascripts/core.js"]
|
28
|
+
|
29
|
+
mattr_accessor :stylesheets
|
30
|
+
@@stylesheets = ["#{File.dirname(__FILE__)}/../../stylesheets/core.css"]
|
31
|
+
|
32
|
+
mattr_accessor :external_css
|
33
|
+
@@external_css = []
|
34
|
+
|
35
|
+
mattr_accessor :ext_location
|
36
|
+
|
37
|
+
mattr_accessor :with_icons
|
38
|
+
|
39
|
+
mattr_accessor :icons_uri
|
40
|
+
@@icons_uri = "/images/icons"
|
41
|
+
|
42
|
+
mattr_accessor :javascript_on_main_page
|
43
|
+
@@javascript_on_main_page = false
|
44
|
+
|
45
|
+
def self.setup
|
46
|
+
yield self
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.reset_components_in_session
|
50
|
+
Netzke::Core.session[:netzke_components].try(:clear)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Array
|
2
|
+
def jsonify
|
3
|
+
self.map{ |el| el.instance_of?(Array) || el.instance_of?(Hash) ? el.jsonify : el }
|
4
|
+
end
|
5
|
+
|
6
|
+
# Camelizes the keys of hashes and converts them to JSON
|
7
|
+
def to_nifty_json
|
8
|
+
self.recursive_delete_if_nil.jsonify.to_json
|
9
|
+
end
|
10
|
+
|
11
|
+
# Applies deep_convert_keys to each element which responds to deep_convert_keys
|
12
|
+
def deep_convert_keys(&block)
|
13
|
+
block_given? ? self.map do |i|
|
14
|
+
i.respond_to?('deep_convert_keys') ? i.deep_convert_keys(&block) : i
|
15
|
+
end : self
|
16
|
+
end
|
17
|
+
|
18
|
+
def deep_each_pair(&block)
|
19
|
+
self.each{ |el| el.respond_to?('deep_each_pair') && el.deep_each_pair(&block) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def recursive_delete_if_nil
|
23
|
+
self.map{|el| el.respond_to?('recursive_delete_if_nil') ? el.recursive_delete_if_nil : el}
|
24
|
+
end
|
25
|
+
|
26
|
+
def deep_freeze
|
27
|
+
each { |j| j.deep_freeze if j.respond_to? :deep_freeze }
|
28
|
+
freeze
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class Hash
|
2
|
+
def deep_each_pair(&block)
|
3
|
+
self.each_pair do |k,v|
|
4
|
+
v.respond_to?('deep_each_pair') ? v.deep_each_pair(&block) : yield(k,v)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
# Recursively convert the keys. Example:
|
9
|
+
# {:bla_bla => 1, "wow_now" => {:look_ma => true}}.deep_convert_keys{|k| k.to_s.camelize.to_sym}
|
10
|
+
# => {:BlaBla => 1, "WowNow" => {:LookMa => true}}
|
11
|
+
def deep_convert_keys(&block)
|
12
|
+
block_given? ? self.inject({}) do |h,(k,v)|
|
13
|
+
h[yield(k)] = v.respond_to?('deep_convert_keys') ? v.deep_convert_keys(&block) : v
|
14
|
+
h
|
15
|
+
end : self
|
16
|
+
end
|
17
|
+
|
18
|
+
def jsonify
|
19
|
+
self.inject({}) do |h,(k,v)|
|
20
|
+
new_key = k.instance_of?(String) || k.instance_of?(Symbol) ? k.jsonify : k
|
21
|
+
new_value = v.instance_of?(Array) || v.instance_of?(Hash) ? v.jsonify : v
|
22
|
+
h.merge(new_key => new_value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# First camelizes the keys, then convert the whole hash to JSON
|
27
|
+
def to_nifty_json
|
28
|
+
self.recursive_delete_if_nil.jsonify.to_json
|
29
|
+
end
|
30
|
+
|
31
|
+
# Converts values of a Hash in such a way that they can be easily stored in the database: hashes and arrays are jsonified, symbols - stringified
|
32
|
+
def deebeefy_values
|
33
|
+
inject({}) do |options, (k, v)|
|
34
|
+
options[k] = v.is_a?(Symbol) ? v.to_s : (v.is_a?(Hash) || v.is_a?(Array)) ? v.to_json : v
|
35
|
+
options
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# We don't need to pass null values in JSON, they are null by simply being absent
|
40
|
+
def recursive_delete_if_nil
|
41
|
+
self.inject({}) do |h,(k,v)|
|
42
|
+
if !v.nil?
|
43
|
+
h[k] = v.respond_to?('recursive_delete_if_nil') ? v.recursive_delete_if_nil : v
|
44
|
+
end
|
45
|
+
h
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# add flatten_with_type method to Hash
|
50
|
+
def flatten_with_type(preffix = "")
|
51
|
+
res = []
|
52
|
+
self.each_pair do |k,v|
|
53
|
+
name = ((preffix.to_s.empty? ? "" : preffix.to_s + "__") + k.to_s).to_sym
|
54
|
+
if v.is_a?(Hash)
|
55
|
+
res += v.flatten_with_type(name)
|
56
|
+
else
|
57
|
+
res << {
|
58
|
+
:name => name,
|
59
|
+
:value => v,
|
60
|
+
:type => (["TrueClass", "FalseClass"].include?(v.class.name) ? 'Boolean' : v.class.name).to_sym
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
res
|
65
|
+
end
|
66
|
+
|
67
|
+
def deep_freeze
|
68
|
+
each { |k,v| v.deep_freeze if v.respond_to? :deep_freeze }
|
69
|
+
freeze
|
70
|
+
end
|
71
|
+
|
72
|
+
# Javascrit-like access to Hash values
|
73
|
+
def method_missing(method, *args)
|
74
|
+
if method.to_s =~ /=$/
|
75
|
+
method_base = method.to_s.sub(/=$/,'').to_sym
|
76
|
+
key = self[method_base.to_s].nil? ? method_base : method_base.to_s
|
77
|
+
self[key] = args.first
|
78
|
+
else
|
79
|
+
key = self[method.to_s].nil? ? method : method.to_s
|
80
|
+
self[key]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class String
|
2
|
+
def jsonify
|
3
|
+
self.camelize(:lower)
|
4
|
+
end
|
5
|
+
|
6
|
+
# Converts self to "literal JSON"-string - one that doesn't get quotes appended when being sent "to_json" method
|
7
|
+
def l
|
8
|
+
ActiveSupport::JSON::Variable.new(self)
|
9
|
+
end
|
10
|
+
|
11
|
+
# removes JS-comments (both single- and multi-line) from the string
|
12
|
+
def strip_js_comments
|
13
|
+
regexp = /\/\/.*$|(?m:\/\*.*?\*\/)/
|
14
|
+
self.gsub!(regexp, '')
|
15
|
+
|
16
|
+
# also remove empty lines
|
17
|
+
regexp = /^\s*\n/
|
18
|
+
self.gsub!(regexp, '')
|
19
|
+
end
|
20
|
+
|
21
|
+
# "false" => false, "whatever_else" => true
|
22
|
+
def to_b
|
23
|
+
self != "false"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|