netzke-core 0.6.4 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/CHANGELOG.rdoc +19 -0
  2. data/README.markdown +43 -0
  3. data/TODO +1 -5
  4. data/app/controllers/netzke_controller.rb +47 -15
  5. data/config/database.yml +2 -0
  6. data/features/component_loader.feature +6 -1
  7. data/features/composition.feature +2 -0
  8. data/features/js_include.feature +18 -0
  9. data/features/nested_views.feature +9 -0
  10. data/features/persistence.feature +6 -4
  11. data/features/support/paths.rb +3 -0
  12. data/javascripts/core.js +166 -519
  13. data/javascripts/ext.js +355 -0
  14. data/javascripts/touch.js +47 -0
  15. data/lib/netzke/actions.rb +31 -38
  16. data/lib/netzke/base.rb +48 -6
  17. data/lib/netzke/composition.rb +52 -63
  18. data/lib/netzke/configuration.rb +6 -2
  19. data/lib/netzke/core/version.rb +2 -2
  20. data/lib/netzke/core.rb +22 -15
  21. data/lib/netzke/javascript/scopes.rb +39 -0
  22. data/lib/netzke/javascript.rb +145 -114
  23. data/lib/netzke/railz/action_view_ext/ext.rb +59 -0
  24. data/lib/netzke/railz/action_view_ext/touch.rb +50 -0
  25. data/lib/netzke/railz/action_view_ext.rb +86 -0
  26. data/lib/netzke/railz/controller_extensions.rb +33 -0
  27. data/lib/netzke/{rails → railz}/routes.rb +0 -0
  28. data/lib/netzke/railz.rb +3 -0
  29. data/lib/netzke/session.rb +18 -3
  30. data/lib/netzke/state.rb +42 -15
  31. data/lib/netzke/stylesheets.rb +23 -8
  32. data/lib/netzke-core.rb +23 -16
  33. data/netzke-core.gemspec +52 -10
  34. data/spec/component/base_spec.rb +11 -0
  35. data/spec/component/javascript_spec.rb +3 -2
  36. data/spec/component/state_spec.rb +18 -0
  37. data/spec/spec_helper.rb +1 -1
  38. data/test/rails_app/Gemfile +3 -2
  39. data/test/rails_app/Gemfile.lock +73 -71
  40. data/test/rails_app/app/components/component_loader.rb +39 -4
  41. data/test/rails_app/app/components/{custom.css → component_with_custom_css/stylesheets/custom.css} +0 -0
  42. data/test/rails_app/app/components/component_with_custom_css.rb +2 -2
  43. data/test/rails_app/app/components/component_with_js_mixin/javascripts/extra_one.js +2 -0
  44. data/test/rails_app/app/components/component_with_js_mixin/javascripts/extra_two.js +2 -0
  45. data/test/rails_app/app/components/component_with_js_mixin/javascripts/method_set_one.js +6 -0
  46. data/test/rails_app/app/components/component_with_js_mixin/javascripts/method_set_two.js +5 -0
  47. data/test/rails_app/app/components/component_with_js_mixin.rb +8 -0
  48. data/test/rails_app/app/components/component_with_session_persistence.rb +10 -3
  49. data/test/rails_app/app/components/extended_component_with_js_mixin/javascripts/some_method_set.js +5 -0
  50. data/test/rails_app/app/components/extended_component_with_js_mixin.rb +7 -0
  51. data/test/rails_app/app/components/hello_world_component.rb +31 -0
  52. data/test/rails_app/app/components/server_caller.rb +1 -1
  53. data/test/rails_app/app/components/simple_panel.rb +2 -0
  54. data/test/rails_app/app/components/touch/hello_world_component.rb +25 -0
  55. data/test/rails_app/app/components/touch/server_caller.rb +28 -0
  56. data/test/rails_app/app/components/touch/simple_carousel.rb +17 -0
  57. data/test/rails_app/app/controllers/components_controller.rb +6 -1
  58. data/test/rails_app/app/controllers/touch_controller.rb +6 -0
  59. data/test/rails_app/app/helpers/touch_helper.rb +2 -0
  60. data/test/rails_app/app/views/components/panel_with_autoload.html.erb +2 -0
  61. data/test/rails_app/app/views/components/some_tab_panel.html.erb +11 -0
  62. data/test/rails_app/app/views/layouts/nested.html.erb +5 -0
  63. data/test/rails_app/app/views/layouts/touch.html.erb +13 -0
  64. data/test/rails_app/config/initializers/netzke.rb +1 -1
  65. data/test/rails_app/config/locales/en.yml +7 -1
  66. data/test/rails_app/config/routes.rb +10 -1
  67. data/test/rails_app/db/migrate/20110110132720_create_netzke_component_states.rb +20 -0
  68. data/test/rails_app/db/schema.rb +14 -1
  69. data/test/rails_app/spec/controllers/touch_controller_spec.rb +5 -0
  70. data/test/rails_app/spec/helpers/touch_helper_spec.rb +15 -0
  71. data/test/unit/netzke_core_test.rb +2 -6
  72. metadata +53 -11
  73. data/README.rdoc +0 -136
  74. data/lib/netzke/rails/action_view_ext.rb +0 -103
  75. data/lib/netzke/rails/controller_extensions.rb +0 -31
  76. data/test/rails_app/db/migrate/20100905214933_create_netzke_preferences.rb +0 -16
data/lib/netzke/base.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext'
2
+ require 'active_support/memoizable'
2
3
  require 'netzke/core_ext'
3
4
  require 'netzke/javascript'
4
5
  require 'netzke/stylesheets'
@@ -47,20 +48,29 @@ module Netzke
47
48
  attr_reader :global_id
48
49
 
49
50
  class << self
51
+ extend ActiveSupport::Memoizable
52
+
50
53
  # Component's short class name, e.g.:
51
54
  # "Netzke::Module::SomeComponent" => "Module::SomeComponent"
52
55
  def short_component_class_name
53
56
  self.name.sub(/^Netzke::/, "")
54
57
  end
55
58
 
56
- # Component's class, given its name
59
+ # Component's class, given its name.
60
+ # Note: this method will be memoized if Rails.configuration.cache_classes is true.
57
61
  def constantize_class_name(class_name)
58
- "#{class_name}".constantize rescue "Netzke::#{class_name}".constantize
62
+ "#{class_name}".constantize
63
+ rescue NameError
64
+ begin
65
+ "Netzke::#{class_name}".constantize
66
+ rescue NameError
67
+ nil
68
+ end
59
69
  end
60
70
 
61
71
  # Instance of component by config
62
72
  def instance_by_config(config)
63
- constantize_class_name(config[:class_name]).new(config)
73
+ (config[:klass] || constantize_class_name(config[:class_name])).new(config)
64
74
  end
65
75
 
66
76
  # All ancestor classes in the Netzke class hierarchy (i.e. up to Netzke::Base)
@@ -78,6 +88,27 @@ module Netzke
78
88
  # We don't want here any values from the superclass (which is the consequence of using inheritable attributes).
79
89
  res == self.superclass.read_inheritable_attribute(attr_name) ? {} : res
80
90
  end
91
+
92
+ # Same as +read_inheritable_attribute+ returning a hash, but returns empty hash when it's equal to superclass's
93
+ def read_clean_inheritable_array(attr_name)
94
+ res = read_inheritable_attribute(attr_name) || []
95
+ # We don't want here any values from the superclass (which is the consequence of using inheritable attributes).
96
+ res == self.superclass.read_inheritable_attribute(attr_name) ? [] : res
97
+ end
98
+ end
99
+
100
+
101
+ def self.total_instances
102
+ @@instances || 0
103
+ end
104
+
105
+ def self.reset_total_instances
106
+ @@instances = 0
107
+ end
108
+
109
+ def self.increase_total_instances
110
+ @@instances ||= 0
111
+ @@instances += 1
81
112
  end
82
113
 
83
114
  # Instantiates a component instance. A parent can optionally be provided.
@@ -92,6 +123,8 @@ module Netzke
92
123
  # initialize @components and @items
93
124
  normalize_components_in_items
94
125
  # auto_collect_actions_from_config_and_js_properties
126
+
127
+ self.class.increase_total_instances
95
128
  end
96
129
 
97
130
  # Proxy to the equally named class method
@@ -108,11 +141,20 @@ module Netzke
108
141
  def before_load
109
142
  end
110
143
 
144
+ def clean_up
145
+ component_session.clear
146
+ components.keys.each { |k| component_instance(k).clean_up }
147
+ end
148
+
149
+ def i18n_id
150
+ self.class.name.split("::").map{|c| c.underscore}.join(".")
151
+ end
152
+
111
153
  private
112
154
 
113
155
  def logger #:nodoc:
114
- if defined?(Rails)
115
- Rails.logger
156
+ if defined?(::Rails)
157
+ ::Rails.logger
116
158
  else
117
159
  require 'logger'
118
160
  Logger.new(STDOUT)
@@ -126,4 +168,4 @@ module Netzke
126
168
  end
127
169
 
128
170
  end
129
- end
171
+ end
@@ -1,6 +1,21 @@
1
1
  # require 'active_support/core_ext/class/inheritable_attributes'
2
2
 
3
3
  module Netzke
4
+ # You can define a nested components by calling the class method +component+:
5
+ #
6
+ # component :users, :data_class => "GridPanel", :model => "User"
7
+ #
8
+ # The method also accepts a block in case you want access to the component's instance:
9
+ #
10
+ # component :books do
11
+ # {:data_class => "Book", :title => build_title}
12
+ # end
13
+ #
14
+ # To override a component, define a method {component_name}_component, e.g.:
15
+ #
16
+ # def books_component
17
+ # super.merge(:title => "Modified Title")
18
+ # end
4
19
  module Composition
5
20
  extend ActiveSupport::Concern
6
21
 
@@ -14,8 +29,8 @@ module Netzke
14
29
  # * <tt>:id</tt> - reference to the component
15
30
  # * <tt>:container</tt> - Ext id of the container where in which the component will be rendered
16
31
  endpoint :deliver_component do |params|
17
- cache = params[:cache].gsub(".", "::").split(",") # array of cached class names (in Ruby)
18
- component_name = params.delete(:name).underscore.to_sym
32
+ cache = params[:cache].split(",") # array of cached xtypes
33
+ component_name = params[:name].underscore.to_sym
19
34
  component = components[component_name] && component_instance(component_name)
20
35
 
21
36
  if component
@@ -38,15 +53,6 @@ module Netzke
38
53
  module ClassMethods
39
54
 
40
55
  # Defines a nested component.
41
- # For example:
42
- #
43
- # component :users, :data_class => "GridPanel", :model => "User"
44
- #
45
- # It can also accept a block (receiving as parameter the eventual definition from super class):
46
- #
47
- # component :books do |orig|
48
- # {:data_class => "Book", :title => orig[:title] + ", extended"}
49
- # end
50
56
  def component(name, config = {}, &block)
51
57
  register_component(name)
52
58
  config = config.dup
@@ -69,6 +75,7 @@ module Netzke
69
75
  end
70
76
  end
71
77
 
78
+ # DEPRECATED in favor of Symbol#component
72
79
  # Component's js config used when embedding components as Container's items
73
80
  # (see some_composite.rb for an example)
74
81
  def js_component(name, config = {})
@@ -91,11 +98,13 @@ module Netzke
91
98
  end
92
99
 
93
100
  module InstanceMethods
101
+ extend ActiveSupport::Memoizable
94
102
 
95
- def items
103
+ def items #:nodoc:
96
104
  @items_with_normalized_components
97
105
  end
98
106
 
107
+ # DEPRECATED in favor of Base.component
99
108
  def initial_components
100
109
  {}
101
110
  end
@@ -105,14 +114,16 @@ module Netzke
105
114
  @components ||= self.class.registered_components.inject({}){ |res, name| res.merge(name.to_sym => send(COMPONENT_METHOD_NAME % name)) }
106
115
  end
107
116
 
108
- def non_late_components
117
+ def eager_loaded_components
109
118
  components.reject{|k,v| v[:lazy_loading]}
110
119
  end
111
120
 
121
+ # DEPRECATED
112
122
  def add_component(aggr)
113
123
  components.merge!(aggr)
114
124
  end
115
125
 
126
+ # DEPRECATED
116
127
  def remove_component(aggr)
117
128
  if config[:persistent_config]
118
129
  persistence_manager_class.delete_all_for_component("#{global_id}__#{aggr}")
@@ -120,51 +131,41 @@ module Netzke
120
131
  components[aggr] = nil
121
132
  end
122
133
 
123
- # 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.
124
- def initial_late_components
125
- {}
126
- end
127
-
128
- def add_late_component(aggr)
129
- components.merge!(aggr.merge(:lazy_loading => true))
130
- end
131
-
132
- # called when the method_missing tries to processes a non-existing component
134
+ # Called when the method_missing tries to processes a non-existing component
133
135
  def component_missing(aggr)
134
136
  flash :error => "Unknown component #{aggr} for component #{name}"
135
137
  {:feedback => @flash}.to_nifty_json
136
138
  end
137
139
 
138
- # 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"
140
+ # Recursively instantiates a component based on its "path": e.g. if we have component :component1 which in its turn has component :component2, the path to the latter would be "component1__component2"
139
141
  def component_instance(name, strong_config = {})
140
- @cached_component_instances ||= {}
141
- @cached_component_instances[name] ||= begin
142
- composite = self
143
- name.to_s.split('__').each do |aggr|
144
- aggr = aggr.to_sym
145
- component_config = composite.components[aggr]
146
- raise ArgumentError, "No component '#{aggr}' defined for component '#{composite.global_id}'" if component_config.nil?
147
- short_component_class_name = component_config[:class_name]
148
- raise ArgumentError, "No class_name specified for component #{aggr} of #{composite.global_id}" if short_component_class_name.nil?
149
- component_class = constantize_class_name(short_component_class_name)
150
-
151
- conf = weak_children_config.
152
- deep_merge(component_config).
153
- deep_merge(strong_config). # we may want to reconfigure the component at the moment of instantiation
154
- merge(:name => aggr)
155
-
156
- composite = component_class.new(conf, composite) # params: config, parent
157
- # composite.weak_children_config = weak_children_config
158
- # composite.strong_children_config = strong_children_config
159
- end
160
- composite
142
+ composite = self
143
+ name.to_s.split('__').each do |cmp|
144
+ cmp = cmp.to_sym
145
+
146
+ component_config = composite.components[cmp]
147
+ raise ArgumentError, "No child component '#{cmp}' defined for component '#{composite.global_id}'" if component_config.nil?
148
+
149
+ component_class_name = component_config[:class_name]
150
+ raise ArgumentError, "No class_name specified for component #{cmp} of #{composite.global_id}" if component_class_name.nil?
151
+
152
+ component_class = constantize_class_name(component_class_name)
153
+ raise ArgumentError, "Unknown constant #{component_class_name}" if component_class.nil?
154
+
155
+ instance_config = weak_children_config.merge(component_config).merge(strong_config).merge(:name => cmp)
156
+
157
+ composite = component_class.new(instance_config, composite) # params: config, parent
161
158
  end
159
+ composite
162
160
  end
163
161
 
162
+ memoize :component_instance # for performance
163
+
164
+ # All components that we depend on (used to render all necessary JavaScript and stylesheets)
164
165
  def dependency_classes
165
166
  res = []
166
167
 
167
- non_late_components.keys.each do |aggr|
168
+ eager_loaded_components.keys.each do |aggr|
168
169
  res += component_instance(aggr).dependency_classes
169
170
  end
170
171
 
@@ -174,19 +175,7 @@ module Netzke
174
175
  res.uniq
175
176
  end
176
177
 
177
- ## Dependencies
178
- # def dependencies
179
- # @dependencies ||= begin
180
- # non_late_components_component_classes = non_late_components.values.map{|v| v[:class_name]}
181
- # (initial_dependencies + non_late_components_component_classes << self.class.short_component_class_name).uniq
182
- # end
183
- # end
184
-
185
- # override this method if you need some extra dependencies, which are not the components
186
- def initial_dependencies
187
- []
188
- end
189
-
178
+ # DEPRECATED
190
179
  def js_component(*args)
191
180
  self.class.js_component(*args)
192
181
  end
@@ -233,9 +222,9 @@ module Netzke
233
222
  end
234
223
  end
235
224
 
236
- private
225
+ protected
237
226
 
238
- def normalize_components(items)
227
+ def normalize_components(items) #:nodoc:
239
228
  @component_index ||= 0
240
229
  @items_with_normalized_components = items.each_with_index.map do |item, i|
241
230
  if is_component_config?(item)
@@ -251,11 +240,11 @@ module Netzke
251
240
  end
252
241
  end
253
242
 
254
- def normalize_components_in_items
243
+ def normalize_components_in_items #:nodoc:
255
244
  normalize_components(config[:items]) if config[:items]
256
245
  end
257
246
 
258
- def is_component_config?(c)
247
+ def is_component_config?(c) #:nodoc:
259
248
  !!(c.is_a?(Hash) && c[:class_name])
260
249
  end
261
250
  end
@@ -60,6 +60,10 @@ module Netzke
60
60
  @strong_config ||= session_config.merge(weak_final_options).merge(strong_parent_config)
61
61
  end
62
62
 
63
+ def configuration
64
+ final_config
65
+ end
66
+
63
67
  # Resulting config that takes into account all possible ways to configure a component. *Read only*.
64
68
  # Translates into something like this:
65
69
  #
@@ -72,7 +76,7 @@ module Netzke
72
76
  # Moved out to a separate method in order to provide for easy caching.
73
77
  # *Do not override this method*, use +Base.config+ instead.
74
78
  def config
75
- @config ||= final_config
79
+ @config ||= configuration
76
80
  end
77
81
 
78
82
  def flat_config(key = nil)
@@ -131,4 +135,4 @@ module Netzke
131
135
 
132
136
  end
133
137
  end
134
- end
138
+ end
@@ -3,9 +3,9 @@ module Netzke
3
3
  module Version
4
4
  MAJOR = 0
5
5
  MINOR = 6
6
- PATCH = 4
6
+ PATCH = 5
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
9
9
  end
10
10
  end
11
- end
11
+ end
data/lib/netzke/core.rb CHANGED
@@ -15,41 +15,48 @@ module Netzke
15
15
  # The following configuration options are available:
16
16
  # * ext_location - absolute path to your Ext code root
17
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
18
  module Core
21
19
  extend Session
22
20
  extend Masquerading
23
21
 
22
+ # Ext or Touch
23
+ mattr_accessor :platform
24
+ @@platform = :ext
25
+
24
26
  # set in Netzke::ControllerExtensions
25
27
  mattr_accessor :controller
26
-
28
+
27
29
  # set in Netzke::ControllerExtensions
28
30
  mattr_accessor :session
29
31
  @@session = {}
30
32
 
31
- mattr_accessor :javascripts
32
- @@javascripts = ["#{File.dirname(__FILE__)}/../../javascripts/core.js"]
33
+ mattr_accessor :ext_javascripts
34
+ @@ext_javascripts = []
35
+
36
+ mattr_accessor :ext_stylesheets
37
+ @@ext_stylesheets = []
33
38
 
34
- mattr_accessor :stylesheets
35
- @@stylesheets = ["#{File.dirname(__FILE__)}/../../stylesheets/core.css"]
39
+ mattr_accessor :touch_javascripts
40
+ @@touch_javascripts = []
36
41
 
37
- mattr_accessor :external_css
38
- @@external_css = []
42
+ mattr_accessor :touch_stylesheets
43
+ @@touch_stylesheets = []
44
+
45
+ # Stylesheets that cannot be loaded dynamically along with the rest of the component, e.g. due to that relative paths are used in them
46
+ mattr_accessor :external_ext_css
47
+ @@external_ext_css = []
39
48
 
40
49
  # Set in the Engine after_initialize callback
41
- mattr_accessor :ext_location
50
+ mattr_accessor :ext_location # TODO: rename to ext_path
51
+ mattr_accessor :touch_location # TODO: rename to touch_path
42
52
  mattr_accessor :with_icons
43
53
 
44
54
  mattr_accessor :icons_uri
45
55
  @@icons_uri = "/images/icons"
46
56
 
47
- mattr_accessor :javascript_on_main_page
48
- @@javascript_on_main_page = true
49
-
50
57
  mattr_accessor :persistence_manager
51
58
  @@persistence_manager = "NetzkeComponentState"
52
-
59
+
53
60
  # Set in the Engine after_initialize callback
54
61
  mattr_accessor :persistence_manager_class
55
62
 
@@ -61,4 +68,4 @@ module Netzke
61
68
  Netzke::Core.session[:netzke_components].try(:clear)
62
69
  end
63
70
  end
64
- end
71
+ end
@@ -0,0 +1,39 @@
1
+ module Netzke
2
+ module Javascript
3
+ module Scopes
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ # Given class name, e.g. GridPanelLib::Components::RecordFormWindow,
8
+ # returns its scope: "Components.RecordFormWindow"
9
+ def js_class_name_to_scope(name)
10
+ name.split("::")[0..-2].join(".")
11
+ end
12
+
13
+ # Top level scope which will be used to scope out Netzke classes
14
+ def js_default_scope
15
+ "Netzke.classes"
16
+ end
17
+
18
+ # Scope of this component without default scope
19
+ # e.g.: GridPanelLib.Components
20
+ def js_scope
21
+ js_class_name_to_scope(short_component_class_name)
22
+ end
23
+
24
+ # Returns the scope of this component
25
+ # e.g. "Netzke.classes.GridPanelLib"
26
+ def js_full_scope
27
+ js_scope.empty? ? js_default_scope : [js_default_scope, js_scope].join(".")
28
+ end
29
+
30
+ # Returns the full name of the JavaScript class, including the scopes *and* the common scope, which is
31
+ # Netzke.classes.
32
+ # E.g.: "Netzke.classes.Netzke.GridPanelLib.RecordFormWindow"
33
+ def js_full_class_name
34
+ [js_full_scope, short_component_class_name.split("::").last].join(".")
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end