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/core_ext.rb
CHANGED
@@ -1,141 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
def deep_convert_keys(&block)
|
7
|
-
block_given? ? self.inject({}) do |h,(k,v)|
|
8
|
-
h[yield(k)] = v.respond_to?('deep_convert_keys') ? v.deep_convert_keys(&block) : v
|
9
|
-
h
|
10
|
-
end : self
|
11
|
-
end
|
12
|
-
|
13
|
-
def jsonify
|
14
|
-
self.inject({}) do |h,(k,v)|
|
15
|
-
new_key = k.instance_of?(String) || k.instance_of?(Symbol) ? k.jsonify : k
|
16
|
-
new_value = v.instance_of?(Array) || v.instance_of?(Hash) ? v.jsonify : v
|
17
|
-
h.merge(new_key => new_value)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# First camelizes the keys, then convert the whole hash to JSON
|
22
|
-
def to_nifty_json
|
23
|
-
self.recursive_delete_if_nil.jsonify.to_json
|
24
|
-
end
|
25
|
-
|
26
|
-
# 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
|
27
|
-
def deebeefy_values
|
28
|
-
inject({}) do |options, (k, v)|
|
29
|
-
options[k] = v.is_a?(Symbol) ? v.to_s : (v.is_a?(Hash) || v.is_a?(Array)) ? v.to_json : v
|
30
|
-
options
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# We don't need to pass null values in JSON, they are null by simply being absent
|
35
|
-
def recursive_delete_if_nil
|
36
|
-
self.inject({}) do |h,(k,v)|
|
37
|
-
if !v.nil?
|
38
|
-
h[k] = v.respond_to?('recursive_delete_if_nil') ? v.recursive_delete_if_nil : v
|
39
|
-
end
|
40
|
-
h
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# add flatten_with_type method to Hash
|
45
|
-
def flatten_with_type(preffix = "")
|
46
|
-
res = []
|
47
|
-
self.each_pair do |k,v|
|
48
|
-
name = ((preffix.to_s.empty? ? "" : preffix.to_s + "__") + k.to_s).to_sym
|
49
|
-
if v.is_a?(Hash)
|
50
|
-
res += v.flatten_with_type(name)
|
51
|
-
else
|
52
|
-
res << {
|
53
|
-
:name => name,
|
54
|
-
:value => v,
|
55
|
-
:type => (["TrueClass", "FalseClass"].include?(v.class.name) ? 'Boolean' : v.class.name).to_sym
|
56
|
-
}
|
57
|
-
end
|
58
|
-
end
|
59
|
-
res
|
60
|
-
end
|
61
|
-
|
62
|
-
# Javascrit-like access to Hash values
|
63
|
-
def method_missing(method, *args)
|
64
|
-
if method.to_s =~ /=$/
|
65
|
-
method_base = method.to_s.sub(/=$/,'').to_sym
|
66
|
-
key = self[method_base.to_s].nil? ? method_base : method_base.to_s
|
67
|
-
self[key] = args.first
|
68
|
-
else
|
69
|
-
key = self[method.to_s].nil? ? method : method.to_s
|
70
|
-
self[key]
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
class Array
|
77
|
-
def jsonify
|
78
|
-
self.map{ |el| el.instance_of?(Array) || el.instance_of?(Hash) ? el.jsonify : el }
|
79
|
-
end
|
80
|
-
|
81
|
-
# Camelizes the keys of hashes and converts them to JSON
|
82
|
-
def to_nifty_json
|
83
|
-
self.recursive_delete_if_nil.jsonify.to_json
|
84
|
-
end
|
85
|
-
|
86
|
-
# Applies deep_convert_keys to each element which responds to deep_convert_keys
|
87
|
-
def deep_convert_keys(&block)
|
88
|
-
block_given? ? self.map do |i|
|
89
|
-
i.respond_to?('deep_convert_keys') ? i.deep_convert_keys(&block) : i
|
90
|
-
end : self
|
91
|
-
end
|
92
|
-
|
93
|
-
def recursive_delete_if_nil
|
94
|
-
self.map{|el| el.respond_to?('recursive_delete_if_nil') ? el.recursive_delete_if_nil : el}
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
class LiteralString < String
|
99
|
-
def to_json(*args)
|
100
|
-
self
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
class String
|
105
|
-
def jsonify
|
106
|
-
self.camelize(:lower)
|
107
|
-
end
|
108
|
-
|
109
|
-
# Converts self to "literal JSON"-string - one that doesn't get quotes appended when being sent "to_json" method
|
110
|
-
def l
|
111
|
-
LiteralString.new(self)
|
112
|
-
end
|
113
|
-
|
114
|
-
# removes JS-comments from the string and minfy it
|
115
|
-
def strip_js_comments
|
116
|
-
Netzke::Base.config[:minify_js] ? JSMin.minify(self) : self
|
117
|
-
end
|
118
|
-
|
119
|
-
# "false" => false, "whatever_else" => true
|
120
|
-
def to_b
|
121
|
-
self != "false"
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
class Symbol
|
126
|
-
def jsonify
|
127
|
-
self.to_s.camelize(:lower).to_sym
|
128
|
-
end
|
129
|
-
|
130
|
-
def l
|
131
|
-
LiteralString.new(self.to_s)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
module ActiveSupport
|
136
|
-
class TimeWithZone
|
137
|
-
def to_json(options = {})
|
138
|
-
self.to_s(:db).to_json
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
1
|
+
require "netzke/core_ext/array"
|
2
|
+
require "netzke/core_ext/hash"
|
3
|
+
require "netzke/core_ext/string"
|
4
|
+
require "netzke/core_ext/symbol"
|
5
|
+
require "netzke/core_ext/time_with_zone"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Netzke
|
2
|
+
# The following methods are used when a component is generated stand-alone (as a part of a HTML page)
|
3
|
+
module Embedding
|
4
|
+
|
5
|
+
# Instantiating
|
6
|
+
def js_component_instance
|
7
|
+
%Q{Netzke.page.#{name.jsonify} = Ext.create(#{js_config.to_nifty_json});}
|
8
|
+
end
|
9
|
+
|
10
|
+
# Rendering
|
11
|
+
def js_component_render
|
12
|
+
%Q{Netzke.page.#{name.jsonify}.render("#{name.to_s.split('_').join('-')}-netzke");} unless self.class.js_xtype == "netzkewindow"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Container for rendering
|
16
|
+
def js_component_html
|
17
|
+
%Q{<div id="#{name.to_s.split('_').join('-')}-netzke" class="netzke-component"></div>}
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Netzke
|
2
|
+
class ExtComponent
|
3
|
+
attr_accessor :name
|
4
|
+
|
5
|
+
def initialize(name, config = {})
|
6
|
+
@name = name
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def config
|
11
|
+
@config ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Rendering
|
15
|
+
def js_component_render
|
16
|
+
%Q{Ext.ComponentMgr.create(#{config.to_nifty_json}).render("ext-#{name.to_s.split('_').join('-')}");}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Container for rendering
|
20
|
+
def js_component_html
|
21
|
+
%Q{<div id="ext-#{name.to_s.split('_').join('-')}" class="ext-component"></div>}
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
module Netzke
|
2
|
+
# == Component javascript code
|
3
|
+
# Here's a brief explanation on how a javascript class for a component gets built.
|
4
|
+
# Component gets defined as a constructor (a function) by +js_class+ class method (see "Inside component's contstructor").
|
5
|
+
# +Ext.extend+ provides inheritance from an Ext class specified in +js_base_class+ class method.
|
6
|
+
#
|
7
|
+
# == Inside component's constructor
|
8
|
+
# * Component's constructor gets called with a parameter that is a configuration object provided by +config+ instance method. This configuration is specific for the instance of the component, and, for example, contains this component's unique id. As another example, by means of this configuration object, a grid receives the configuration array for its columns, a form - for its fields, etc.
|
9
|
+
module Javascript
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
class_attribute :js_included_files
|
14
|
+
self.js_included_files = []
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
|
19
|
+
# The JS (Ext) class that we inherit from on the JS level
|
20
|
+
def js_base_class(class_name = nil)
|
21
|
+
class_name.nil? ? (read_inheritable_attribute(:js_base_class) || "Ext.Panel") : write_inheritable_attribute(:js_base_class, class_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Definition of a public JS method, e.g.:
|
25
|
+
# js_method :on_call_server, <<-JS
|
26
|
+
# function(){
|
27
|
+
# this.whatsUp();
|
28
|
+
# }
|
29
|
+
# JS
|
30
|
+
def js_method(name, definition = nil)
|
31
|
+
definition = yield.l if block_given?
|
32
|
+
current_js_methods = read_clean_inheritable_hash(:js_methods)
|
33
|
+
current_js_methods.merge!(name => definition.l)
|
34
|
+
write_inheritable_attribute(:js_methods, current_js_methods)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns all JS method definitions in a hash
|
38
|
+
def js_methods
|
39
|
+
read_clean_inheritable_hash(:js_methods)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Definition of JS files which will be dynamically loaded together with this component, e.g.:
|
43
|
+
# js_include "#{File.dirname(__FILE__)}/my_component/static.js"
|
44
|
+
# or
|
45
|
+
# js_include "#{File.dirname(__FILE__)}/my_component/one.js","#{File.dirname(__FILE__)}/my_component/two.js"
|
46
|
+
# This is alternative to defining self.include_js
|
47
|
+
def js_include(*args)
|
48
|
+
self.js_included_files += args
|
49
|
+
end
|
50
|
+
|
51
|
+
# Definition of a public JS property, e.g.:
|
52
|
+
# js_property :title, "My Netzke Component"
|
53
|
+
def js_property(name, value = nil)
|
54
|
+
name = name.to_sym
|
55
|
+
if value.nil?
|
56
|
+
(read_inheritable_attribute(:js_properties) || {})[name]
|
57
|
+
else
|
58
|
+
current_js_properties = read_clean_inheritable_hash(:js_properties)
|
59
|
+
current_js_properties[name] = value
|
60
|
+
write_inheritable_attribute(:js_properties, current_js_properties)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Assignment of multiple public JS properties in a bunch, e.g.:
|
65
|
+
# js_properties :title => "My Component", :border => true, :html => "Inner HTML"
|
66
|
+
def js_properties(hsh = nil)
|
67
|
+
if hsh.nil?
|
68
|
+
read_clean_inheritable_hash(:js_properties)
|
69
|
+
else
|
70
|
+
current_js_properties = read_clean_inheritable_hash(:js_properties)
|
71
|
+
current_js_properties.merge!(hsh)
|
72
|
+
write_inheritable_attribute(:js_properties, current_js_properties)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# JS properties and methods merged together
|
77
|
+
def js_extend_properties
|
78
|
+
@_js_extend_properties ||= js_properties.merge(js_methods)
|
79
|
+
# res = js_properties.merge(js_methods)
|
80
|
+
# extracted_actions = extract_actions(res)
|
81
|
+
# res.merge!(:actions => extracted_actions) if !extracted_actions.empty?
|
82
|
+
# res
|
83
|
+
# end
|
84
|
+
end
|
85
|
+
|
86
|
+
# TODO: the code below needs refactoring and cleaning-up
|
87
|
+
|
88
|
+
# component's menus
|
89
|
+
# def js_menus; []; end
|
90
|
+
|
91
|
+
# Given class name, e.g. GridPanelLib::Components::RecordFormWindow,
|
92
|
+
# returns its scope: "Components.RecordFormWindow"
|
93
|
+
def js_class_name_to_scope(name)
|
94
|
+
name.split("::")[0..-2].join(".")
|
95
|
+
end
|
96
|
+
|
97
|
+
# Top level scope which will be used to scope out Netzke classes
|
98
|
+
def js_default_scope
|
99
|
+
"Netzke.classes"
|
100
|
+
end
|
101
|
+
|
102
|
+
# Scope of this component without default scope
|
103
|
+
# e.g.: GridPanelLib.Components
|
104
|
+
def js_scope
|
105
|
+
js_class_name_to_scope(short_component_class_name)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns the scope of this component
|
109
|
+
# e.g. "Netzke.classes.GridPanelLib"
|
110
|
+
def js_full_scope
|
111
|
+
js_scope.empty? ? js_default_scope : [js_default_scope, js_scope].join(".")
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns the full name of the JavaScript class, including the scopes *and* the common scope, which is
|
115
|
+
# Netzke.classes.
|
116
|
+
# E.g.: "Netzke.classes.Netzke.GridPanelLib.RecordFormWindow"
|
117
|
+
def js_full_class_name
|
118
|
+
[js_full_scope, short_component_class_name.split("::").last].join(".")
|
119
|
+
end
|
120
|
+
|
121
|
+
# Builds this component's xtype
|
122
|
+
# E.g.: netzkewindow, netzkegridpanel
|
123
|
+
def js_xtype
|
124
|
+
name.gsub("::", "").downcase
|
125
|
+
end
|
126
|
+
|
127
|
+
# are we using JS inheritance? for now, if js_base_class is a Netzke class - yes
|
128
|
+
def extends_netzke_component?
|
129
|
+
superclass != Netzke::Base
|
130
|
+
end
|
131
|
+
|
132
|
+
# Declaration of component's class (stored in the cache storage (Ext.netzke.cache) at the client side
|
133
|
+
# to be reused at the moment of component instantiation)
|
134
|
+
def js_class(cached = [])
|
135
|
+
res = []
|
136
|
+
# Defining the scope if it isn't known yet
|
137
|
+
res << %{Ext.ns("#{js_full_scope}");} unless js_full_scope == js_default_scope
|
138
|
+
|
139
|
+
res << (extends_netzke_component? ? js_class_declaration_extending_component : js_class_declaration_new_component)
|
140
|
+
|
141
|
+
res << %(Ext.reg("#{js_xtype}", #{js_full_class_name});)
|
142
|
+
|
143
|
+
res.join("\n")
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def js_extra_code
|
148
|
+
""
|
149
|
+
end
|
150
|
+
|
151
|
+
# Generates declaration of the JS class as direct extension of a Ext component
|
152
|
+
def js_class_declaration_new_component
|
153
|
+
%(#{js_full_class_name} = Ext.extend(#{js_base_class}, Ext.apply(Netzke.componentMixin(#{js_base_class}),
|
154
|
+
#{js_extend_properties.to_nifty_json}));)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Generates declaration of the JS class as extension of another Netzke component
|
158
|
+
def js_class_declaration_extending_component
|
159
|
+
base_class = superclass.js_full_class_name
|
160
|
+
|
161
|
+
# Do we specify our own extend properties?
|
162
|
+
# If so, include them, if not - don't re-include those from the parent.
|
163
|
+
js_extend_properties.empty? ? \
|
164
|
+
%{#{js_full_class_name} = #{base_class};} :
|
165
|
+
%{#{js_full_class_name} = Ext.extend(#{base_class}, #{js_extend_properties.to_nifty_json});}
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns all extra JavaScript-code (as string) required by this component's class
|
169
|
+
def js_included
|
170
|
+
res = ""
|
171
|
+
|
172
|
+
# Prevent re-including code that was already included by the parent
|
173
|
+
# (thus, only include those JS files when include_js was defined in the current class, not in its ancestors)
|
174
|
+
((singleton_methods(false).map(&:to_sym).include?(:include_js) ? include_js : []) + js_included_files).each do |path|
|
175
|
+
f = File.new(path)
|
176
|
+
res << f.read << "\n"
|
177
|
+
end
|
178
|
+
|
179
|
+
res
|
180
|
+
end
|
181
|
+
|
182
|
+
def include_js
|
183
|
+
[]
|
184
|
+
end
|
185
|
+
|
186
|
+
# JavaScript code needed for this particulaer class. Includes external JS code and the JS class definition for self.
|
187
|
+
def js_code(cached = [])
|
188
|
+
[js_included, js_class(cached)].join("\n")
|
189
|
+
end
|
190
|
+
|
191
|
+
# Little helper
|
192
|
+
def this; "this".l; end
|
193
|
+
|
194
|
+
# Little helper
|
195
|
+
def null; "null".l; end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
module InstanceMethods
|
200
|
+
# Config that is used for instantiating the component in javascript
|
201
|
+
def js_config
|
202
|
+
res = {}
|
203
|
+
|
204
|
+
# Unique id of the component
|
205
|
+
res.merge!(:id => global_id)
|
206
|
+
|
207
|
+
# Non-late components
|
208
|
+
comp_hash = {}
|
209
|
+
non_late_components.each_pair do |comp_name, comp_config|
|
210
|
+
comp_instance = component_instance(comp_name.to_sym)
|
211
|
+
comp_instance.before_load
|
212
|
+
comp_hash[comp_name] = comp_instance.js_config
|
213
|
+
end
|
214
|
+
|
215
|
+
# All our non-lazy-loaded children are specified here, while in +items+ we barely reference them, because
|
216
|
+
# +items+, generally, only contain a subset of all non-lazy-loaded children.
|
217
|
+
res[:components] = comp_hash unless comp_hash.empty?
|
218
|
+
|
219
|
+
# Api (besides the default "deliver_component" - JavaScript side already knows about it)
|
220
|
+
endpoints = self.class.endpoints - [:deliver_component]
|
221
|
+
res.merge!(:endpoints => endpoints) unless endpoints.empty?
|
222
|
+
|
223
|
+
# Inform the JavaScript side if persistent_config is enabled
|
224
|
+
# res[:persistent_config] = persistence_enabled?
|
225
|
+
|
226
|
+
# Include our xtype
|
227
|
+
res[:xtype] = self.class.js_xtype
|
228
|
+
|
229
|
+
# Merge with the rest of config options, besides those that are only meant for the server side
|
230
|
+
res.merge!(config.reject{ |k,v| self.class.server_side_config_options.include?(k.to_sym) })
|
231
|
+
|
232
|
+
res[:items] = items unless items.blank?
|
233
|
+
|
234
|
+
res
|
235
|
+
end
|
236
|
+
|
237
|
+
# All the JS-code required by this instance of the component to be instantiated in the browser.
|
238
|
+
# It includes JS-classes for the parents, non-lazy-loaded child components, and itself.
|
239
|
+
def js_missing_code(cached = [])
|
240
|
+
code = dependency_classes.inject("") do |r,k|
|
241
|
+
cached.include?(k.to_s) ? r : r + k.js_code(cached)#.strip_js_comments
|
242
|
+
end
|
243
|
+
code.blank? ? nil : code
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Netzke
|
2
|
+
# TODO:
|
3
|
+
# rename persistence_ to persistence_
|
4
|
+
module Persistence
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
# Persistent config manager class
|
8
|
+
def persistence_manager_class
|
9
|
+
Netzke::Base.persistence_manager.try(:constantize)
|
10
|
+
rescue NameError
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
|
18
|
+
# If the component has persistent config in its disposal
|
19
|
+
def persistence_enabled?
|
20
|
+
!persistence_manager_class.nil? && initial_config[:persistent_config]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Access to own persistent config, e.g.:
|
24
|
+
# persistent_config["window.size"] = 100
|
25
|
+
# persistent_config["window.size"] => 100
|
26
|
+
# This method is user/role-aware
|
27
|
+
# def persistent_config
|
28
|
+
# if persistence_enabled?
|
29
|
+
# config_class = self.class.persistence_manager_class
|
30
|
+
# config_class.component_name = persistence_key.to_s # pass to the config class our unique name
|
31
|
+
# config_class
|
32
|
+
# else
|
33
|
+
# # if we can't use presistent config, all the calls to it will always return nil,
|
34
|
+
# # and the "="-operation will be ignored
|
35
|
+
# logger.debug "==> NETZKE: no persistent config is set up for component '#{global_id}'"
|
36
|
+
# {}
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
|
40
|
+
# Access to the global persistent config (e.g. of another component)
|
41
|
+
def global_persistent_config(owner = nil)
|
42
|
+
config_class = self.class.persistence_manager_class
|
43
|
+
config_class.component_name = owner
|
44
|
+
config_class
|
45
|
+
end
|
46
|
+
|
47
|
+
# A string which will identify NetzkePreference records for this component.
|
48
|
+
# If <tt>persistence_key</tt> is passed, use it. Otherwise use global component's id.
|
49
|
+
def persistence_key #:nodoc:
|
50
|
+
# 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
|
51
|
+
initial_config[:persistence_key] ? initial_config[:persistence_key] : global_id.to_sym
|
52
|
+
end
|
53
|
+
|
54
|
+
# def update_persistent_ext_config(hsh)
|
55
|
+
# current_config = persistent_config[:ext_config] || {}
|
56
|
+
# current_config.deep_merge!(hsh.deep_convert_keys{ |k| k.to_s }) # first, recursively stringify the keys
|
57
|
+
# persistent_config[:ext_config] = current_config
|
58
|
+
# end
|
59
|
+
|
60
|
+
# Returns a hash built from all persistent config values for the current component, following the double underscore
|
61
|
+
# naming convention. E.g., if we have the following persistent config pairs:
|
62
|
+
# enabled => true
|
63
|
+
# layout__width => 100
|
64
|
+
# layout__header__height => 20
|
65
|
+
#
|
66
|
+
# this method will return the following hash:
|
67
|
+
# {:enabled => true, :layout => {:width => 100, :header => {:height => 20}}}
|
68
|
+
# def persistence_hash_OLD
|
69
|
+
# return {} if !persistence_enabled?
|
70
|
+
#
|
71
|
+
# @persistence_hash ||= begin
|
72
|
+
# prefs = NetzkePreference.find_all_for_component(persistence_key.to_s)
|
73
|
+
# res = {}
|
74
|
+
# prefs.each do |p|
|
75
|
+
# hsh_levels = p.name.split("__").map(&:to_sym)
|
76
|
+
# tmp_res = {} # it decends into itself, building itself
|
77
|
+
# anchor = {} # it will keep the tail of tmp_res
|
78
|
+
# hsh_levels.each do |level_prefix|
|
79
|
+
# tmp_res[level_prefix] ||= level_prefix == hsh_levels.last ? p.normalized_value : {}
|
80
|
+
# anchor = tmp_res[level_prefix] if level_prefix == hsh_levels.first
|
81
|
+
# tmp_res = tmp_res[level_prefix]
|
82
|
+
# end
|
83
|
+
# # Now 'anchor' is a hash that represents the path to the single value,
|
84
|
+
# # for example: {:ext_config => {:title => 100}} (which corresponds to ext_config__title)
|
85
|
+
# # So we need to recursively merge it into the final result
|
86
|
+
# res.deep_merge!(hsh_levels.first => anchor)
|
87
|
+
# end
|
88
|
+
# res.deep_convert_keys{ |k| k.to_sym } # recursively symbolize the keys
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
|
92
|
+
def update_persistent_options(hash)
|
93
|
+
if persistence_enabled?
|
94
|
+
options = persistent_options
|
95
|
+
persistence_manager_class.pref_to_write(global_id).update_attribute(:value, options.deep_merge(hash))
|
96
|
+
else
|
97
|
+
logger && logger.debug("Netzke warning: No persistence enabled for component '#{global_id}'")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def persistent_options
|
102
|
+
return {} if !persistence_enabled?
|
103
|
+
persistence_manager_class.pref_to_read(global_id).try(:value) || {}
|
104
|
+
end
|
105
|
+
|
106
|
+
# A convenience method for instances
|
107
|
+
def persistence_manager_class
|
108
|
+
self.class.persistence_manager_class
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.included(receiver)
|
114
|
+
receiver.extend ClassMethods
|
115
|
+
receiver.send :include, InstanceMethods
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Netzke
|
2
|
+
module ActionViewExt
|
3
|
+
# Include JavaScript
|
4
|
+
def netzke_js_include
|
5
|
+
res = []
|
6
|
+
|
7
|
+
# ExtJS
|
8
|
+
res << (ENV['RAILS_ENV'] == 'development' ? javascript_include_tag("/extjs/adapter/ext/ext-base-debug", "/extjs/ext-all-debug") : javascript_include_tag("/extjs/adapter/ext/ext-base", "/extjs/ext-all"))
|
9
|
+
|
10
|
+
# Netzke (dynamically generated)
|
11
|
+
res << javascript_include_tag("/netzke/netzke")
|
12
|
+
res.join("\n")
|
13
|
+
end
|
14
|
+
|
15
|
+
# Include CSS
|
16
|
+
def netzke_css_include(theme_name = "default")
|
17
|
+
# ExtJS base
|
18
|
+
res = stylesheet_link_tag("/extjs/resources/css/ext-all")
|
19
|
+
# ExtJS theming
|
20
|
+
res << "\n" << stylesheet_link_tag("/extjs/resources/css/xtheme-#{theme_name}") unless theme_name.to_s == "default"
|
21
|
+
# Netzke (dynamically generated)
|
22
|
+
res << "\n" << stylesheet_link_tag("/netzke/netzke")
|
23
|
+
|
24
|
+
# External stylesheets (which cannot be loaded dynamically along with the rest of the component, e.g. due to that
|
25
|
+
# relative paths are used in them)
|
26
|
+
res << "\n" << stylesheet_link_tag(Netzke::Core.external_css)
|
27
|
+
|
28
|
+
res
|
29
|
+
end
|
30
|
+
|
31
|
+
# JavaScript for all Netzke classes in this view, and Ext.onReady which renders all Netzke components in this view
|
32
|
+
def netzke_js
|
33
|
+
res = []
|
34
|
+
if Netzke::Core.javascript_on_main_page
|
35
|
+
res << content_for(:netzke_js_classes)
|
36
|
+
res << "\n"
|
37
|
+
end
|
38
|
+
res << "Ext.onReady(function(){"
|
39
|
+
res << content_for(:netzke_on_ready)
|
40
|
+
res << "});"
|
41
|
+
|
42
|
+
javascript_tag res.join("\n")
|
43
|
+
end
|
44
|
+
|
45
|
+
def netzke_css
|
46
|
+
%{
|
47
|
+
<style type="text/css" media="screen">
|
48
|
+
#{content_for(:netzke_css)}
|
49
|
+
</style>}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Wrapper for all the above. Use it in your layout.
|
53
|
+
# Params: <tt>:ext_theme</tt> - the name of ExtJS theme to apply (optional)
|
54
|
+
# E.g.:
|
55
|
+
# <%= netzke_init :ext_theme => "grey" %>
|
56
|
+
def netzke_init(params = {})
|
57
|
+
theme = params[:ext_theme] || :default
|
58
|
+
raw([netzke_css_include(theme), netzke_css, netzke_js_include, netzke_js].join("\n"))
|
59
|
+
end
|
60
|
+
|
61
|
+
# Use this helper in your views to embed Netzke components. E.g.:
|
62
|
+
# netzke :my_grid, :class_name => "Basepack::GridPanel", :columns => [:id, :name, :created_at]
|
63
|
+
# On how to configure a component, see documentation for Netzke::Base or/and specific component
|
64
|
+
def netzke(name, config = {})
|
65
|
+
@rendered_classes ||= []
|
66
|
+
|
67
|
+
# if we are the first netzke call on the page, reset components hash in the session
|
68
|
+
if @rendered_classes.empty?
|
69
|
+
Netzke::Core.reset_components_in_session
|
70
|
+
end
|
71
|
+
|
72
|
+
class_name = config[:class_name] ||= name.to_s.camelcase
|
73
|
+
|
74
|
+
config[:name] = name
|
75
|
+
|
76
|
+
Netzke::Core.reg_component(config)
|
77
|
+
|
78
|
+
w = Netzke::Base.instance_by_config(config)
|
79
|
+
w.before_load # inform the component about initial load
|
80
|
+
|
81
|
+
if Netzke::Core.javascript_on_main_page
|
82
|
+
content_for :netzke_js_classes, raw(w.js_missing_code(@rendered_classes))
|
83
|
+
end
|
84
|
+
|
85
|
+
content_for :netzke_css, raw(w.css_missing_code(@rendered_classes))
|
86
|
+
|
87
|
+
content_for :netzke_on_ready, raw("#{w.js_component_instance}\n\n#{w.js_component_render}")
|
88
|
+
|
89
|
+
# Now mark this component's class as rendered, so that we only generate it once per view
|
90
|
+
@rendered_classes << class_name unless @rendered_classes.include?(class_name)
|
91
|
+
|
92
|
+
# Return the html for this component
|
93
|
+
raw(w.js_component_html)
|
94
|
+
end
|
95
|
+
|
96
|
+
def ext(name, config = {})
|
97
|
+
comp = Netzke::ExtComponent.new(name, config)
|
98
|
+
content_for :netzke_on_ready, raw("#{comp.js_component_render}")
|
99
|
+
raw(comp.js_component_html)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -2,12 +2,13 @@ module Netzke
|
|
2
2
|
module ControllerExtensions
|
3
3
|
def self.included(base)
|
4
4
|
base.send(:before_filter, :set_session_data)
|
5
|
+
base.send(:before_filter, :set_controller)
|
5
6
|
end
|
6
7
|
|
7
8
|
def set_session_data
|
8
|
-
Netzke::
|
9
|
+
::Netzke::Core.session = session
|
9
10
|
session[:netzke_user_id] = defined?(current_user) ? current_user.try(:id) : nil
|
10
|
-
# set netzke_just_logged_in and netzke_just_logged_out states (may be used by Netzke
|
11
|
+
# set netzke_just_logged_in and netzke_just_logged_out states (may be used by Netzke components)
|
11
12
|
if session[:_netzke_next_request_is_first_after_login]
|
12
13
|
session[:netzke_just_logged_in] = true
|
13
14
|
session[:_netzke_next_request_is_first_after_login] = false
|
@@ -22,5 +23,9 @@ module Netzke
|
|
22
23
|
session[:netzke_just_logged_out] = false
|
23
24
|
end
|
24
25
|
end
|
26
|
+
|
27
|
+
def set_controller
|
28
|
+
::Netzke::Core.controller = self
|
29
|
+
end
|
25
30
|
end
|
26
31
|
end
|