0

So, I have a standard class that is extended by child classes. In some cases, I'm passing what child class to use through jQuery as a option instead of using the default standard class.

Example: jQuery('img').myPlugin();

Calls

New mainClass(arg1, arg2)

While

jQuery('img').myPlugin({classToUse : 'myExtendedClass'});

Calls

New myExtendedClass(arg1, arg2)

Here's the code that works, but it is ugly in so many ways. How can we do this properly? Here @settings is a simple object, and @settings.classToUse is a string that's passed.

@NewClass = eval("new #{@settings.classToUse}(this.$element, this.settings)")

The following does not work and returns a string error:

@Framer = new @settings['classToUse'](this.$element, this.settings)

A clipped version of the full source code is below. Any errors in the logic are a result of clipping the code as the code in its current state is 100% functional.

You can also see the code compiled here.

class mainClass
  constructor: (@framedObject, @options) ->
  doSomething: ->
    alert('something')

class myExtendedClass extends mainClass
  doSomething: ->
    alert('somethingDifferent')

class anotherExtendedClass extends mainClass
  doSomething: ->
    super
    alert('Woah!')

$ ->
  $.plugin = (element, options) ->
    @settings = {classToUse : 'myExtendedClass'} #built by merging defaults and options
    @init = ->      
      @mainClass = eval("new #{@settings.classToUse}(this.$element, this.settings)")
    @init()
    this
  $.fn.mediaFrame = (options) ->
    return this.each ->
      plugin = new $.plugin this, options

2 Answers 2

3

How about something like:

class Border
  constructor: (@options) ->
class RedBorder extends Border
    constructor: (@options) ->
class BlueBorder extends Border
    constructor: (@options) ->

borders = 
  Default: Border
  Red: RedBorder
  Blue: BlueBorder

$.plugin = (element, options) ->
  @mainClass = new borders[@settings.class || 'Default'](@settings)
Sign up to request clarification or add additional context in comments.

5 Comments

Mostly, I just want to call "new Border". Using the child classes are only for exceptions in certain circumstances. I've updated my question to include this. And sticking everything in an object, or using an if/else isn't much cleaner than using eval().
@AaronHarun how is using an object not cleaner than eval()? Am I not seeing the big picture there?
1) eval is strongly discouraged as you open up for code injections. 2) by explictly putting your classes into an hash your building a visible dependency. eval will simply confuse anybody including you in 3 months time
@AaronHarun: Using an object also makes error handling a lot cleaner and easier to trap yourself.
With the changes, it's better than eval. Though, coffeescript should eally have a better way to handle variable classes.
0

your second approach will fail as you cant instantiate a "String"

 @Framer = new @settings['classToUse'](this.$element, this.settings)

also there seems to be an error as you are using "type" instead of "classToUse"

The easiest way to would seem to simply put the class-object into the hash that you give to jQuery

 jQuery('img').myPlugin({classToUse : redBorder});

In this example assuming that redBorder is the class name.

2 Comments

The 'type' typo was just from quickly rewriting all of the code to make more sense outside my plugin. Passing the class object directly doesn't work, since it isn't a global (and I wont't make it one).
well, if you dont make those classes global how will they ever eval correctly? btw. what do you mean by "global"? you could define those classes in a local "namespace" easily

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.