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.
Files changed (152) hide show
  1. data/.autotest +1 -1
  2. data/.gitignore +4 -2
  3. data/CHANGELOG.rdoc +37 -15
  4. data/README.rdoc +93 -28
  5. data/Rakefile +5 -7
  6. data/TODO +24 -3
  7. data/app/controllers/netzke_controller.rb +70 -0
  8. data/app/models/netzke_preference.rb +170 -0
  9. data/autotest/discover.rb +2 -3
  10. data/features/actions.feature +13 -0
  11. data/features/basic.feature +7 -0
  12. data/features/client-server.feature +15 -0
  13. data/features/complex_component.feature +18 -0
  14. data/features/component.feature +13 -0
  15. data/features/component_loader.feature +31 -0
  16. data/features/composition.feature +32 -0
  17. data/features/custom_css.feature +17 -0
  18. data/features/file_inclusion.feature +13 -0
  19. data/features/inheritance.feature +19 -0
  20. data/features/persistence.feature +16 -0
  21. data/features/scopes.feature +17 -0
  22. data/features/step_definitions/custom_css_steps.rb +7 -0
  23. data/features/step_definitions/generic_steps.rb +15 -0
  24. data/features/step_definitions/web_steps.rb +219 -0
  25. data/features/support/env.rb +62 -0
  26. data/features/support/paths.rb +45 -0
  27. data/generators/netzke_core/USAGE +3 -3
  28. data/generators/netzke_core/netzke_core_generator.rb +8 -0
  29. data/install.rb +1 -1
  30. data/javascripts/core.js +542 -499
  31. data/lib/generators/migration_helper.rb +32 -0
  32. data/lib/generators/netzke/USAGE +9 -0
  33. data/lib/generators/netzke/core_generator.rb +24 -0
  34. data/lib/netzke/actions.rb +102 -0
  35. data/lib/netzke/base.rb +77 -529
  36. data/lib/netzke/composition.rb +251 -0
  37. data/lib/netzke/configuration.rb +134 -0
  38. data/lib/netzke/core/masquerading.rb +34 -0
  39. data/lib/netzke/core/session.rb +30 -0
  40. data/lib/netzke/core/version.rb +11 -0
  41. data/lib/netzke/core.rb +53 -0
  42. data/lib/netzke/core_ext/array.rb +30 -0
  43. data/lib/netzke/core_ext/hash.rb +84 -0
  44. data/lib/netzke/core_ext/string.rb +26 -0
  45. data/lib/netzke/core_ext/symbol.rb +13 -0
  46. data/lib/netzke/core_ext/time_with_zone.rb +7 -0
  47. data/lib/netzke/core_ext.rb +5 -141
  48. data/lib/netzke/embedding.rb +21 -0
  49. data/lib/netzke/ext_component.rb +25 -0
  50. data/lib/netzke/javascript.rb +248 -0
  51. data/lib/netzke/persistence.rb +118 -0
  52. data/lib/netzke/rails/action_view_ext.rb +103 -0
  53. data/lib/netzke/{controller_extensions.rb → rails/controller_extensions.rb} +7 -2
  54. data/lib/netzke/rails/routes.rb +7 -0
  55. data/lib/netzke/services.rb +68 -0
  56. data/lib/netzke/session.rb +35 -0
  57. data/lib/netzke/stylesheets.rb +52 -0
  58. data/lib/netzke-core.rb +28 -29
  59. data/netzke-core.gemspec +253 -0
  60. data/spec/component/actions_spec.rb +94 -0
  61. data/spec/component/base_spec.rb +25 -0
  62. data/spec/component/composition_spec.rb +132 -0
  63. data/spec/component/javascript_spec.rb +15 -0
  64. data/spec/core_ext_spec.rb +16 -0
  65. data/spec/spec.opt +2 -0
  66. data/spec/spec_helper.rb +35 -0
  67. data/{test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb → templates/core/create_netzke_preferences.rb} +4 -4
  68. data/test/rails_app/.gitignore +4 -0
  69. data/test/rails_app/Gemfile +36 -0
  70. data/test/rails_app/Gemfile.lock +137 -0
  71. data/test/rails_app/README +15 -0
  72. data/test/rails_app/Rakefile +7 -0
  73. data/test/rails_app/app/components/border_layout_panel.rb +4 -0
  74. data/test/rails_app/app/components/component_loader.rb +59 -0
  75. data/test/rails_app/app/components/component_with_actions.rb +58 -0
  76. data/test/rails_app/app/components/component_with_custom_css.rb +8 -0
  77. data/test/rails_app/app/components/component_with_included_js.rb +16 -0
  78. data/test/rails_app/app/components/component_with_session_persistence.rb +25 -0
  79. data/test/rails_app/app/components/custom.css +3 -0
  80. data/test/rails_app/app/components/deprecated/server_caller.rb +20 -0
  81. data/test/rails_app/app/components/extended_component_with_actions.rb +5 -0
  82. data/test/rails_app/app/components/extended_server_caller.rb +18 -0
  83. data/test/rails_app/app/components/included.js +5 -0
  84. data/test/rails_app/app/components/kinda_complex_component/basic_stuff.rb +35 -0
  85. data/test/rails_app/app/components/kinda_complex_component/extra_stuff.rb +16 -0
  86. data/test/rails_app/app/components/kinda_complex_component.rb +7 -0
  87. data/test/rails_app/app/components/loader_of_component_with_custom_css.rb +15 -0
  88. data/test/rails_app/app/components/scoped_components/deep_scoped_components/some_deep_scoped_component.rb +7 -0
  89. data/test/rails_app/app/components/scoped_components/extended_scoped_component.rb +5 -0
  90. data/test/rails_app/app/components/scoped_components/some_scoped_component.rb +5 -0
  91. data/test/rails_app/app/components/server_caller.rb +21 -0
  92. data/test/rails_app/app/components/simple_component.rb +5 -0
  93. data/test/rails_app/app/components/simple_tab_panel.rb +27 -0
  94. data/test/rails_app/app/components/simple_window.rb +3 -0
  95. data/test/rails_app/app/components/some_composite.rb +65 -0
  96. data/test/rails_app/app/components/some_ext_component.rb +8 -0
  97. data/test/{app_root → rails_app}/app/controllers/application_controller.rb +1 -0
  98. data/test/rails_app/app/controllers/components_controller.rb +12 -0
  99. data/test/rails_app/app/controllers/multiple_components_controller.rb +2 -0
  100. data/test/rails_app/app/controllers/welcome_controller.rb +5 -0
  101. data/test/rails_app/app/helpers/application_helper.rb +2 -0
  102. data/test/rails_app/app/views/layouts/application.html.erb +13 -0
  103. data/test/rails_app/app/views/multiple_components/set_one.html.erb +3 -0
  104. data/test/rails_app/config/application.rb +45 -0
  105. data/test/rails_app/config/boot.rb +13 -0
  106. data/test/rails_app/config/database.yml +22 -0
  107. data/test/rails_app/config/environment.rb +6 -0
  108. data/test/rails_app/config/environments/development.rb +22 -0
  109. data/test/rails_app/config/environments/production.rb +49 -0
  110. data/test/rails_app/config/environments/test.rb +35 -0
  111. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  112. data/test/rails_app/config/initializers/inflections.rb +10 -0
  113. data/test/rails_app/config/initializers/mime_types.rb +5 -0
  114. data/test/rails_app/config/initializers/netzke.rb +3 -0
  115. data/test/rails_app/config/initializers/secret_token.rb +7 -0
  116. data/test/rails_app/config/initializers/session_store.rb +8 -0
  117. data/test/rails_app/config/locales/en.yml +5 -0
  118. data/test/rails_app/config/routes.rb +64 -0
  119. data/test/rails_app/config.ru +4 -0
  120. data/test/rails_app/db/development_structure.sql +4 -0
  121. data/{generators/netzke_core/templates/create_netzke_preferences.rb → test/rails_app/db/migrate/20100905214933_create_netzke_preferences.rb} +1 -3
  122. data/test/rails_app/db/schema.rb +24 -0
  123. data/test/rails_app/db/seeds.rb +7 -0
  124. data/test/{app_root/config/environments/in_memory.rb → rails_app/lib/tasks/.gitkeep} +0 -0
  125. data/test/rails_app/public/404.html +26 -0
  126. data/test/rails_app/public/422.html +26 -0
  127. data/test/rails_app/public/500.html +26 -0
  128. data/test/{app_root/config/environments/mysql.rb → rails_app/public/favicon.ico} +0 -0
  129. data/test/rails_app/public/images/rails.png +0 -0
  130. data/test/rails_app/public/robots.txt +5 -0
  131. data/test/rails_app/script/rails +6 -0
  132. data/test/{app_root/config/environments/postgresql.rb → rails_app/vendor/plugins/.gitkeep} +0 -0
  133. data/test/unit/netzke_core_test.rb +74 -75
  134. data/test/unit/netzke_preference_test.rb +2 -2
  135. metadata +176 -48
  136. data/lib/app/controllers/netzke_controller.rb +0 -46
  137. data/lib/app/models/netzke_preference.rb +0 -171
  138. data/lib/netzke/action_view_ext.rb +0 -81
  139. data/lib/netzke/base_js.rb +0 -339
  140. data/lib/netzke/routing.rb +0 -9
  141. data/test/app_root/app/models/role.rb +0 -3
  142. data/test/app_root/app/models/user.rb +0 -3
  143. data/test/app_root/config/boot.rb +0 -115
  144. data/test/app_root/config/database.yml +0 -31
  145. data/test/app_root/config/environment.rb +0 -14
  146. data/test/app_root/config/environments/sqlite.rb +0 -0
  147. data/test/app_root/config/environments/sqlite3.rb +0 -0
  148. data/test/app_root/config/routes.rb +0 -4
  149. data/test/app_root/db/migrate/20090423214303_create_roles.rb +0 -11
  150. data/test/app_root/db/migrate/20090423222114_create_users.rb +0 -12
  151. data/test/app_root/lib/console_with_fixtures.rb +0 -4
  152. data/test/app_root/script/console +0 -7
@@ -1,141 +1,5 @@
1
- class Hash
2
-
3
- # Recursively convert the keys. Example:
4
- # {:bla_bla => 1, "wow_now" => {:look_ma => true}}.deep_convert_keys{|k| k.to_s.camelize.to_sym}
5
- # => {:BlaBla => 1, "WowNow" => {:LookMa => true}}
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::Base.session = session
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 widgets)
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
@@ -0,0 +1,7 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ def netzke
4
+ match "/netzke/:action(.:format)" => "netzke"
5
+ end
6
+ end
7
+ end