Skip to content

Commit

Permalink
Some code clean-up and documenting. New scenario in component_loader.…
Browse files Browse the repository at this point in the history
…feature.
  • Loading branch information
Sergei Kozlov committed Jan 7, 2011
1 parent 57a8680 commit d717f10
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 85 deletions.
7 changes: 6 additions & 1 deletion features/component_loader.feature
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ Feature: Component loader
When I press "Load with feedback"
Then I should see "Callback invoked!"


@selenium
Scenario: Component loader should load a window component with another component in it
Given I am on the ComponentLoader test page
When I press "Load window with simple component"
Then I should see "Simple Component Inside Window"
And I should see "Inner text"

@selenium
Scenario: Component loader should load a component with params properly
Given I am on the ComponentLoader test page
When I press "Load with params"
Then I should see "Simple Component with changed HTML"


1 change: 1 addition & 0 deletions features/composition.feature
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Feature: Composition
Then I should see "Here's an update for west panel"

When I press "Update east south from server"
And I sleep 1 second
Then I should see "Here's an update for south panel in east panel"


23 changes: 11 additions & 12 deletions lib/netzke/actions.rb
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
module Netzke
# Netzke component allows specifying Ext actions.
# An action can be defined in 2 different ways, both of which result in a method definition like this
# def _<some_action>_action
# ...
# end
#
# Netzke components allow specifying Ext actions.
# The 2 ways to define an action are:
# * as a hash:
# action :bug_server, :text => "Call server", :icon => "/images/icons/phone.png"
# * as a hash, e.g:
#
# action :bug_server, :text => "Call server", :icon => :phone
#
# * as a block:
# (if the same action was defined in the super class, the superclass's definition get merged with the current definition)
#
# * as a block, in case you need access to the component's instance, e.g.:
# action :bug_server do
# {:text => config[:text], :disabled => true}
# end
#
# The block can optionally receive the configuration of an action being overridden:
# action :bug_server do |orig|
# {:text => config[:text] + orig[:text], :disabled => orig[:disabled]}
# Both of the ways result in a definition of an instance method named {action_name}_action. So, overriding an action in the child class is done be redefining the method, e.g.:
#
# def bug_server_action
# # super will have the superclass's action definition
# end
module Actions
extend ActiveSupport::Concern
Expand Down
114 changes: 51 additions & 63 deletions lib/netzke/composition.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
# require 'active_support/core_ext/class/inheritable_attributes'

module Netzke
# You can define a nested components by calling the class method +component+:
#
# component :users, :data_class => "GridPanel", :model => "User"
#
# The method also accepts a block in case you want access to the component's instance:
#
# component :books do
# {:data_class => "Book", :title => build_title}
# end
#
# To override a component, define a method {component_name}_component, e.g.:
#
# def books_component
# super.merge(:title => "Modified Title")
# end
module Composition
extend ActiveSupport::Concern

Expand All @@ -15,7 +30,7 @@ module Composition
# * <tt>:container</tt> - Ext id of the container where in which the component will be rendered
endpoint :deliver_component do |params|
cache = params[:cache].split(",") # array of cached xtypes
component_name = params.delete(:name).underscore.to_sym
component_name = params[:name].underscore.to_sym
component = components[component_name] && component_instance(component_name)

if component
Expand All @@ -38,15 +53,6 @@ module Composition
module ClassMethods

# Defines a nested component.
# For example:
#
# component :users, :data_class => "GridPanel", :model => "User"
#
# It can also accept a block (receiving as parameter the eventual definition from super class):
#
# component :books do |orig|
# {:data_class => "Book", :title => orig[:title] + ", extended"}
# end
def component(name, config = {}, &block)
register_component(name)
config = config.dup
Expand All @@ -69,6 +75,7 @@ def component(name, config = {}, &block)
end
end

# DEPRECATED in favor of Symbol#component
# Component's js config used when embedding components as Container's items
# (see some_composite.rb for an example)
def js_component(name, config = {})
Expand All @@ -91,11 +98,13 @@ def registered_components
end

module InstanceMethods
extend ActiveSupport::Memoizable

def items
def items #:nodoc:
@items_with_normalized_components
end

# DEPRECATED in favor of Base.component
def initial_components
{}
end
Expand All @@ -105,67 +114,58 @@ def components
@components ||= self.class.registered_components.inject({}){ |res, name| res.merge(name.to_sym => send(COMPONENT_METHOD_NAME % name)) }
end

def non_late_components
def eager_loaded_components
components.reject{|k,v| v[:lazy_loading]}
end

# DEPRECATED
def add_component(aggr)
components.merge!(aggr)
end

# DEPRECATED
def remove_component(aggr)
if config[:persistent_config]
persistence_manager_class.delete_all_for_component("#{global_id}__#{aggr}")
end
components[aggr] = nil
end

# 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.
def initial_late_components
{}
end

def add_late_component(aggr)
components.merge!(aggr.merge(:lazy_loading => true))
end

# called when the method_missing tries to processes a non-existing component
# Called when the method_missing tries to processes a non-existing component
def component_missing(aggr)
flash :error => "Unknown component #{aggr} for component #{name}"
{:feedback => @flash}.to_nifty_json
end

# recursively instantiates an component based on its "path": e.g. if we have component :aggr1 which in its turn has component :aggr10, the path to the latter would be "aggr1__aggr10"
# 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"
def component_instance(name, strong_config = {})
@cached_component_instances ||= {}
@cached_component_instances[name] ||= begin
composite = self
name.to_s.split('__').each do |aggr|
aggr = aggr.to_sym
component_config = composite.components[aggr]
raise ArgumentError, "No child component '#{aggr}' defined for component '#{composite.global_id}'" if component_config.nil?
component_class_name = component_config[:class_name]
raise ArgumentError, "No class_name specified for component #{aggr} of #{composite.global_id}" if component_class_name.nil?
component_class = constantize_class_name(component_class_name)
raise ArgumentError, "Unknown constant #{component_class_name}" if component_class.nil?

conf = weak_children_config.
deep_merge(component_config).
deep_merge(strong_config). # we may want to reconfigure the component at the moment of instantiation
merge(:name => aggr)

composite = component_class.new(conf, composite) # params: config, parent
# composite.weak_children_config = weak_children_config
# composite.strong_children_config = strong_children_config
end
composite
composite = self
name.to_s.split('__').each do |cmp|
cmp = cmp.to_sym

component_config = composite.components[cmp]
raise ArgumentError, "No child component '#{cmp}' defined for component '#{composite.global_id}'" if component_config.nil?

component_class_name = component_config[:class_name]
raise ArgumentError, "No class_name specified for component #{cmp} of #{composite.global_id}" if component_class_name.nil?

component_class = constantize_class_name(component_class_name)
raise ArgumentError, "Unknown constant #{component_class_name}" if component_class.nil?

instance_config = weak_children_config.merge(component_config).merge(strong_config).merge(:name => cmp)

composite = component_class.new(instance_config, composite) # params: config, parent
end
composite
end

memoize :component_instance # for performance

# All components that we depend on (used to render all necessary JavaScript and stylesheets)
def dependency_classes
res = []

non_late_components.keys.each do |aggr|
eager_loaded_components.keys.each do |aggr|
res += component_instance(aggr).dependency_classes
end

Expand All @@ -175,19 +175,7 @@ def dependency_classes
res.uniq
end

## Dependencies
# def dependencies
# @dependencies ||= begin
# non_late_components_component_classes = non_late_components.values.map{|v| v[:class_name]}
# (initial_dependencies + non_late_components_component_classes << self.class.short_component_class_name).uniq
# end
# end

# override this method if you need some extra dependencies, which are not the components
def initial_dependencies
[]
end

# DEPRECATED
def js_component(*args)
self.class.js_component(*args)
end
Expand Down Expand Up @@ -234,9 +222,9 @@ def method_missing(method_name, params = {})
end
end

private
protected

def normalize_components(items)
def normalize_components(items) #:nodoc:
@component_index ||= 0
@items_with_normalized_components = items.each_with_index.map do |item, i|
if is_component_config?(item)
Expand All @@ -252,11 +240,11 @@ def normalize_components(items)
end
end

def normalize_components_in_items
def normalize_components_in_items #:nodoc:
normalize_components(config[:items]) if config[:items]
end

def is_component_config?(c)
def is_component_config?(c) #:nodoc:
!!(c.is_a?(Hash) && c[:class_name])
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/netzke/javascript.rb
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ def js_config

# Non-lazy-loaded components
comp_hash = {}
non_late_components.each_pair do |comp_name, comp_config|
eager_loaded_components.each_pair do |comp_name, comp_config|
comp_instance = component_instance(comp_name.to_sym)
comp_instance.before_load
comp_hash[comp_name] = comp_instance.js_config
Expand Down
2 changes: 1 addition & 1 deletion netzke-core.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Gem::Specification.new do |s|

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Sergei Kozlov"]
s.date = %q{2011-01-06}
s.date = %q{2011-01-08}
s.description = %q{Allows building ExtJS/Rails reusable code in a DRY way}
s.email = %q{[email protected]}
s.extra_rdoc_files = [
Expand Down
17 changes: 16 additions & 1 deletion test/rails_app/app/components/component_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ class ComponentLoader < Netzke::Base

action :load_composite

action :load_with_params

js_properties(
:title => "Component Loader",
:layout => "fit",
:bbar => [{:text => "Load component", :ref => "../button"}, {:text => "Load in window", :ref => "../loadInWindowButton"}, :load_with_feedback.action, :load_window_with_simple_component.action, :load_composite.action]
:bbar => [{:text => "Load component", :ref => "../button"}, {:text => "Load in window", :ref => "../loadInWindowButton"}, :load_with_feedback.action, :load_window_with_simple_component.action, :load_composite.action, :load_with_params.action]
)

js_method :on_load_window_with_simple_component, <<-JS
Expand All @@ -50,6 +52,12 @@ class ComponentLoader < Netzke::Base
}
JS

js_method :on_load_with_params, <<-JS
function(params){
this.loadComponent({name: "simple_component", params: {html: "Simple Component with changed HTML"}});
}
JS

js_method :init_component, <<-JS
function(){
#{js_full_class_name}.superclass.initComponent.call(this);
Expand All @@ -70,6 +78,13 @@ class ComponentLoader < Netzke::Base
}
JS

def deliver_component_endpoint(params)
if params[:name] == "simple_component" && params[:html]
components[:simple_component].merge!(:html => params[:html])
end
super
end

# For visual testing purposes
# def deliver_component_endpoint(params)
# sleep 2
Expand Down
8 changes: 2 additions & 6 deletions test/unit/netzke_core_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@ def self.config
})
end

def initial_components
{
:nested_one => {:class_name => 'NestedComponentOne'},
:nested_two => {:class_name => 'NestedComponentTwo'}
}
end
component :nested_one, :class_name => 'NestedComponentOne'
component :nested_two, :class_name => 'NestedComponentTwo'

def available_permissions
%w(read update)
Expand Down

0 comments on commit d717f10

Please sign in to comment.