Module: JRubyFX::DSL

Overview

Defines a nice DSL for building JavaFX applications. Include it in a class for access to the DSL. JRubyFX::Application and JRubyFX::Controller include it already.

Defined Under Namespace

Modules: ClassUtils

Constant Summary collapse

NAME_TO_CLASSES =

– FIXME: This should be broken up with nice override for each type of fx object so we can manually create static overrides. ++ The list of snake_case names mapped to full java classes to use for DSL mapping. This list is dynamically generated using JRubyFX::FXImports::JFX_CLASS_HIERARCHY and Hash.flat_tree_inject.

{
# observable structs
'observable_array_list' => proc { |*args| FXCollections.observable_array_list(*args) },
'double_property' => SimpleDoubleProperty,
'xy_chart_series' => Java::javafx.scene.chart.XYChart::Series,
'xy_chart_data' => Java::javafx.scene.chart.XYChart::Data,
    }.merge(JFX_CLASS_HIERARCHY.flat_tree_inject(Hash) do |res, name, values|
  # Merge in auto-generated list of classes from all the imported classes
  unless values.is_a? Hash
    values.map do |i|
      # this regexp does snake_casing
      # TODO: Anybody got a better way to get the java class instead of evaling its name?
      res.merge!({i.snake_case.gsub(/(h|v)_(line|box)/, '\1\2') => eval(i)})
    end
    res
  else
    # we are not at a leaf node anymore, merge in previous work
    res.merge!(values)
  end
end)
ENUM_OVERRIDES =

List of known overrides for enums.

{PathTransition::OrientationType => {:orthogonal_to_tangent => :orthogonal},
BlendMode => {:src_over => :over, :src_atop => :atop, :color_dodge => :dodge, :color_burn => :burn},
ContentDisplay => {:graphic_only => :graphic, :text_only => :text},
BlurType => {:one_pass_box => [:one, :one_pass], :two_pass_box => [:two, :two_pass], :three_pass_box => [:three, :three_pass]},
Modality => {:window_modal => :window, :application_modal => [:application, :app]}}

Constants included from JRubyFX

VERSION

Constants included from FXImports

FXImports::JFX_CLASS_HIERARCHY

Class Method Summary collapse

Instance Method Summary collapse

Methods included from JRubyFX

#build, #run_later, #with

Methods included from Utils::CommonUtils

#attempt_conversion, #populate_properties, #split_args_from_properties

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object Also known as: node_method_missing

This is the heart of the DSL. When a method is missing and the name of the method is in the NAME_TO_CLASSES mapping, it calls JRubyFX.build with the Java class. This means that instead of saying

build(JavaClass, hash) { ... }

you can say

java_class(hash) { ... }

Another major portion of the DSL is the ability to implicitly add new created components to their parent on construction. There are a few places where this is undesirable. In order to prevent implicit construction you can add a ‘!’ on the end:

circle!(30)

This will construct a Circle but it will not add it into its parent container. This is useful for specifying clipping regions in particular.



171
172
173
174
175
176
# File 'lib/jrubyfx/dsl.rb', line 171

def method_missing(name, *args, &block)
  clazz = NAME_TO_CLASSES[name.to_s.gsub(/!$/, '')]
  super unless clazz
  
  build(clazz, *args, &block)
end

Class Method Details

.included(mod) ⇒ Object

When a class includes JRubyFX, extend (add to the metaclass) ClassUtils



117
118
119
# File 'lib/jrubyfx/dsl.rb', line 117

def self.included(mod)
  mod.extend(JRubyFX::DSL::ClassUtils)
end

.inject_enum_method_converter(jfunc, in_class) ⇒ Object

“overrides” given function name in given class to parse ruby symbols into proper enums. Rewrites method name as ‘my_method=` from `setMyMethod`



233
234
235
236
237
238
239
240
241
242
243
# File 'lib/jrubyfx/dsl.rb', line 233

def self.inject_enum_method_converter(jfunc, in_class)
  jclass = in_class.java_class.java_instance_methods.find_all {|i| i.name == jfunc.to_s}[0].argument_types[0]
  jclass = JavaUtilities.get_proxy_class(jclass)
  
  # Define the conversion function as the snake cased assignment, calling parse_ruby
  in_class.class_eval do
    define_method "#{jfunc.to_s.gsub(/^set/i,'').snake_case}=" do |rbenum|
      java_send jfunc, [jclass], jclass.parse_ruby_symbols(rbenum)
    end
  end
end

.inject_symbol_converter(jclass) ⇒ Object

Adds ‘parse_ruby_symbols` method to given enum/class to enable symbol conversion



220
221
222
223
224
225
226
227
228
229
# File 'lib/jrubyfx/dsl.rb', line 220

def self.inject_symbol_converter(jclass)
  # inject!
  class << jclass
    define_method :parse_ruby_symbols do |const|
      # cache it. It could be expensive
      @map = JRubyFX::Utils::CommonConverters.map_enums(self) if @map == nil
      @map[const.to_s] || const
    end
  end
end

.load_dslObject

This loads the entire DSL. Call this immediately after requiring this file, but not inside this file, or it requires itself twice.



247
248
249
250
251
252
253
254
# File 'lib/jrubyfx/dsl.rb', line 247

def self.load_dsl
  rt = "#{File.dirname(__FILE__)}/core_ext"
  Dir.glob("#{rt}/*.rb") do |file|
    require file
  end
  
  JRubyFX::DSL.load_enum_converter()
end

.load_enum_converterObject

Loads the special symbol to enum converter functions into all methods and enums



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/jrubyfx/dsl.rb', line 182

def self.load_enum_converter
  # load overrides
  ENUM_OVERRIDES.each do |cls, overrides|
    JRubyFX::Utils::CommonConverters.set_overrides_for cls, overrides
  end
  
  # use reflection to load all enums into all_enums and methods that use them
  # into enum_methods
  mod_list = {
    :methods => [],
    :all => []
  }
  JRubyFX::DSL::NAME_TO_CLASSES.each do |n,cls|
    cls.java_class.java_instance_methods.each do |method|
      args = method.argument_types.find_all(&:enum?).tap {|i| mod_list[:all] <<  i }
      
      # one and only, must be a setter style
      if method.argument_types.length == 1 and (args.length == method.argument_types.length)
        mod_list[:methods] << [method.name, cls]
      end
    end if cls.respond_to? :ancestors and cls.ancestors.include? JavaProxy # some are not java classes. ignore those
  end
  
  # Get the proper class (only need them once)
  mod_list[:all] = mod_list[:all].flatten.uniq.map {|i| JavaUtilities.get_proxy_class(i) }
  
  # Inject our converter into each enum/class
  mod_list[:all].each do |enum|
    inject_symbol_converter enum
  end
  
  # finally, "override" each method
  mod_list[:methods].each do |method|
    inject_enum_method_converter *method
  end
end