Class: Netzke::Core::ClientClass

Inherits:
Object
  • Object
show all
Defined in:
lib/netzke/core/client_class.rb

Overview

This class is responsible of creation of the client (JavaScript) class. It is passed as block parameter to the `js_configure` DSL method:

class MyComponent < Netzke::Base
  js_configure do |c|
    c.extend = "Ext.form.Panel"
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(klass) ⇒ ClientClass



13
14
15
16
17
18
19
20
21
22
23
# File 'lib/netzke/core/client_class.rb', line 13

def initialize(klass)
  @klass = klass
  @required_files = []
  @mixins = []
  @properties = {
    extend: extended_class,
    alias: class_alias,
  }
  @properties[:mixins] = ['Netzke.classes.Core.Mixin'] if extending_extjs_component?
  @translated_properties = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object

Allows assigning JavaScript prototype properties, including functions:

class MyComponent < Netzke::Base
  js_configure do |c|
    # this will result in the +title+ property defined on the client class prototype
    c.title = "My cool component"

    # this will result in the +onButtonPress+ function defined on the client class prototype
    c.on_button_press = <<-JS
      function(){
        // ...
      }
    JS
  end
end

An alternative way to define prototype properties is by using “mixins”, see #mixin

Class attributes are accessible from inside js_configure:

class MyComponent < Netzke::Base
  class_attribute :title
  self.title = "Some default title"
  js_configure do |c|
    c.title = self.title
  end
end

Now you can configure your component on a class level like this:

# e.g. in Rails initializers
MyComponent.title = "New title for all MyComponents"

Or using a helper method provided by Netzke:

MyComponent.setup do |config|
  config.title = "New title for all MyComponents"
end


63
64
65
66
67
68
69
70
# File 'lib/netzke/core/client_class.rb', line 63

def method_missing(name, *args)
  if name =~ /(.+)=$/
    value = args.first
    @properties[$1.to_sym] = value.is_a?(String) && value =~ /^\s*function/ ? Netzke::Core::JsonLiteral.new(value) : value
  else
    @properties[name.to_sym]
  end
end

Instance Attribute Details

#base_classObject

Returns the value of attribute base_class



11
12
13
# File 'lib/netzke/core/client_class.rb', line 11

def base_class
  @base_class
end

#mixinsObject

Returns the value of attribute mixins



11
12
13
# File 'lib/netzke/core/client_class.rb', line 11

def mixins
  @mixins
end

#propertiesObject

Returns the value of attribute properties



11
12
13
# File 'lib/netzke/core/client_class.rb', line 11

def properties
  @properties
end

#required_filesObject

Returns the value of attribute required_files



11
12
13
# File 'lib/netzke/core/client_class.rb', line 11

def required_files
  @required_files
end

#translated_propertiesObject

Returns the value of attribute translated_properties



11
12
13
# File 'lib/netzke/core/client_class.rb', line 11

def translated_properties
  @translated_properties
end

Instance Method Details

#alias_prefixObject

Alias prefix: 'widget' for components, 'plugin' for plugins



224
225
226
# File 'lib/netzke/core/client_class.rb', line 224

def alias_prefix
  @klass < Netzke::Plugin ? "plugin" : "widget"
end

#class_aliasObject

The alias, required by Ext.Component, e.g.: widget.helloworld



149
150
151
# File 'lib/netzke/core/client_class.rb', line 149

def class_alias
  [alias_prefix, xtype].join(".")
end

#class_codeObject

Component's JavaScript class declaration. It gets stored in the JS class cache storage (Netzke.classes) at the client side to be reused at the moment of component instantiation.



161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/netzke/core/client_class.rb', line 161

def class_code
  res = []
  # Defining the scope if it isn't known yet
  res << %{Ext.ns("#{scope}");} unless scope == default_scope

  res << class_declaration

  # Store created class xtype in the cache
  res << %(
Netzke.cache.push('#{xtype}');
)

  res.join("\n")
end

#class_declarationObject

Generates declaration of the JS class as direct extension of a Ext component



219
220
221
# File 'lib/netzke/core/client_class.rb', line 219

def class_declaration
%(Ext.define('#{class_name}', #{properties_as_string});)
end

#class_nameObject

Returns the full name of the JavaScript class, including the scopes and the common scope, which is 'Netzke.classes'. E.g.: “Netzke.classes.Netzke.Basepack.GridPanel”



189
190
191
# File 'lib/netzke/core/client_class.rb', line 189

def class_name
  [scope, @klass.name.split("::").last].join(".")
end

#code_with_dependenciesObject

JavaScript code needed for this particulaer class. Includes external JS code and the JS class definition for self.



214
215
216
# File 'lib/netzke/core/client_class.rb', line 214

def code_with_dependencies
  [required, class_code].join("\n")
end

#default_scopeObject

Top level scope which will be used to scope out Netzke classes



177
178
179
# File 'lib/netzke/core/client_class.rb', line 177

def default_scope
  "Netzke.classes"
end

#extended_classObject

Default extended class



242
243
244
# File 'lib/netzke/core/client_class.rb', line 242

def extended_class
  extending_extjs_component? ? "Ext.panel.Panel" : @klass.superclass.js_config.class_name
end

#extending_extjs_component?Boolean

Whether we have to inherit from an Ext JS component, or a Netzke component



194
195
196
# File 'lib/netzke/core/client_class.rb', line 194

def extending_extjs_component?
  @klass.superclass == Netzke::Base
end

#mixin(*args) ⇒ Object

Use it to “mixin” JavaScript objects defined in a separate file. It may accept one or more symbols or strings.

Symbols will be expanded following a convention, e.g.:

class MyComponent < Netzke::Base
  js_configure do |c|
    c.mixin :some_functionality
    #...
  end
end

This will “mixin” a JavaScript object defined in a file named {component_location}/my_component/javascripts/some_functionality.js, which way contain something like this:

{
  someProperty: 100,
  someMethod: function(params){
    // ...
  }
}

Also accepts a string, which will be interpreted as a full path to the file (useful for sharing mixins between classes). With no parameters, will assume :component_class_name_underscored.

Also, see defining JavaScript prototype properties with #method_missing.



121
122
123
124
125
126
127
# File 'lib/netzke/core/client_class.rb', line 121

def mixin(*args)
  args << @klass.name.split("::").last.underscore.to_sym if args.empty?
  callr = caller.first
  args.each do |a|
    @mixins << (a.is_a?(Symbol) ? expand_require_path(a, callr) : a)
  end
end

#mixins_as_stringObject



228
229
230
231
232
233
234
235
# File 'lib/netzke/core/client_class.rb', line 228

def mixins_as_string
  mixins.presence && mixins.map do |f|
    as_string = File.read(f)
    as_string.sub!('{', ' ')
    as_string[as_string.rindex('}')] = ' '
    as_string.rstrip
  end.join(",\n")
end

#properties_as_stringObject



237
238
239
# File 'lib/netzke/core/client_class.rb', line 237

def properties_as_string
  [properties.netzke_jsonify.to_json.chop,  mixins_as_string].compact.join(",\n") + "}"
end

#require(*args) ⇒ Object

Use it to specify JavaScript files to be loaded before this component's JavaScript code. Useful when using external extensions required by this component.

It may accept one or more symbols or strings.

Symbols will be expanded following a convention, e.g.:

class MyComponent < Netzke::Base
  js_configure do |c|
    c.require :some_library
  end
end

This will “require” a JavaScript file {component_location}/my_component/javascripts/some_library.js

Strings will be interpreted as full paths to the required JavaScript file:

js_configure do |c|
  c.require "#{File.dirname(__FILE__)}/my_component/one.js", "#{File.dirname(__FILE__)}/my_component/two.js"
end


91
92
93
94
95
# File 'lib/netzke/core/client_class.rb', line 91

def require(*args)
  callr = caller.first

  @required_files |= args.map{ |a| a.is_a?(Symbol) ? expand_require_path(a, callr) : a }
end

#requiredObject

Returns all required JavaScript files as a string



199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/netzke/core/client_class.rb', line 199

def required
  res = ""

  # Prevent re-including code that was already required by the parent
  # (thus, only require those JS files when require_js was defined in the current class, not in its ancestors)
  # FIXME!
  ((@klass.singleton_methods(false).map(&:to_sym).include?(:include_js) ? include_js : []) + required_files).each do |path|
    f = File.new(path)
    res << f.read << "\n"
  end

  res
end

#scopeObject

Returns the scope of this component e.g. “Netzke.classes.Netzke.Basepack”



183
184
185
# File 'lib/netzke/core/client_class.rb', line 183

def scope
  [default_scope, *@klass.name.split("::")[0..-2]].join(".")
end

#translate(*args) ⇒ Object

Defines the “i18n” config property, that is a translation object for this component, such as:

i18n: {
  overwriteConfirm: "Are you sure you want to overwrite preset '{0}'?",
  overwriteConfirmTitle: "Overwriting preset",
  deleteConfirm: "Are you sure you want to delete preset '{0}'?",
  deleteConfirmTitle: "Deleting preset"
}

E.g.:

class MyComponent < Netzke::Base
  js_configure do |c|
    c.translate :overwrite_confirm, :overwrite_confirm_title, :delete_confirm, :delete_confirm_title
  end
end


144
145
146
# File 'lib/netzke/core/client_class.rb', line 144

def translate(*args)
  @translated_properties |= args
end

#xtypeObject

Builds this component's xtype E.g.: netzkebasepackwindow, netzkebasepackgridpanel



155
156
157
# File 'lib/netzke/core/client_class.rb', line 155

def xtype
  @klass.name.gsub("::", "").downcase
end