Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 7221

Show
Ignore:
Timestamp:
07/24/07 17:24:25 (1 year ago)
Author:
sam
Message:

prototype: Template enhancements. Closes #8166.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • spinoffs/prototype/trunk/CHANGELOG

    r7219 r7221  
    11*SVN* 
    22 
    3 * Extended grep semantics. The first argument to Enumerable#grep is now a "filter" (an object with a match() method) so you can now e.g. filter an array of DOM nodes by CSS selector.  RegExp#match is now an alias to RegExp#test, so grep can still be used to filter an array of strings with a regular expression.  Closes #7596.  [Christophe Porteneuve, sam] 
     3* Template enhancements.  Closes #8166.  [Christophe Porteneuve] 
     4  - Added String#interpolate as a shortcut for new Template(...).evaluate(...).  
     5  - If you pass String#interpolate or Template#evaluate an object with a toTemplateReplacements() method, the return value of that method will be used as the replacement object. 
     6  - You can now substitute properties of template replacement values in template strings, using dot or bracket notation (or both).  Example: 
     7    "#{name.last}, #{name.first[0]}. (#{location})".interpolate({ 
     8      name: { first: "Christophe", last: "Porteneuve" }, location: "Paris" 
     9    }) // "Porteneuve, C. (Paris)" 
     10 
     11* Extended grep semantics.  The first argument to Enumerable#grep is now a "filter" (an object with a match() method) so you can now e.g. filter an array of DOM nodes by CSS selector.  RegExp#match is now an alias to RegExp#test, so grep can still be used to filter an array of strings with a regular expression.  Closes #7596.  [Christophe Porteneuve, sam] 
    412 
    513* Make String#scan explicitly return a string. This prevents possible issues with methods expecting input data that is typeof == 'string'. Closes #6350. [AndrewRev, Tobie Langel] 
  • spinoffs/prototype/trunk/src/string.js

    r7218 r7221  
    200200  blank: function() { 
    201201    return /^\s*$/.test(this); 
     202  }, 
     203 
     204  interpolate: function(object, pattern) { 
     205    return new Template(this, pattern).evaluate(object); 
    202206  } 
    203207}); 
     
    232236  initialize: function(template, pattern) { 
    233237    this.template = template.toString(); 
    234     this.pattern = pattern || Template.Pattern; 
     238    this.pattern = pattern || Template.Pattern; 
    235239  }, 
    236240   
    237241  evaluate: function(object) { 
     242    if (typeof object.toTemplateReplacements == 'function') 
     243      object = object.toTemplateReplacements(); 
     244 
    238245    return this.template.gsub(this.pattern, function(match) { 
    239       var before = match[1]; 
     246      if (object == null) return ''; 
     247       
     248      var before = match[1] || ''; 
    240249      if (before == '\\') return match[2]; 
    241       return before + String.interpret(object[match[3]]); 
    242     }); 
    243   } 
    244 
     250       
     251      var ctx = object, expr = match[3]; 
     252      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/, match = pattern.exec(expr); 
     253      if (match == null) return ''; 
     254 
     255      while (match != null) { 
     256        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; 
     257        ctx = ctx[comp]; 
     258        if (null == ctx || '' == match[3]) break; 
     259        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); 
     260        match = pattern.exec(expr); 
     261      } 
     262       
     263      return before + String.interpret(ctx); 
     264    }.bind(this)); 
     265  } 
     266}; 
  • spinoffs/prototype/trunk/test/unit/string.html

    r7218 r7221  
    147147      assertEnumEqual(['f','o','o'],'foo'.toArray()); 
    148148    }}, 
    149      
     149 
    150150    /*  
    151151      Note that camelize() differs from its Rails counterpart, 
     
    314314        template.evaluate(falses)); 
    315315    }}, 
    316      
     316 
     317    testTemplateEvaluationWithNested: function() {with(this) { 
     318      var source = '#{name} #{manager.name} #{manager.age} #{manager.undef} #{manager.age.undef} #{colleagues.first.name}'; 
     319      var subject = { manager: { name: 'John', age: 29 }, name: 'Stephan', age: 22, colleagues: { first: { name: 'Mark' } } }; 
     320      assertEqual('Stephan', new Template('#{name}').evaluate(subject)); 
     321      assertEqual('John', new Template('#{manager.name}').evaluate(subject)); 
     322      assertEqual('29', new Template('#{manager.age}').evaluate(subject)); 
     323      assertEqual('', new Template('#{manager.undef}').evaluate(subject)); 
     324      assertEqual('', new Template('#{manager.age.undef}').evaluate(subject)); 
     325      assertEqual('Mark', new Template('#{colleagues.first.name}').evaluate(subject)); 
     326      assertEqual('Stephan John 29   Mark', new Template(source).evaluate(subject)); 
     327    }}, 
     328 
     329    testTemplateEvaluationWithIndexing: function() {with(this) { 
     330      var source = '#{0} = #{[0]} - #{1} = #{[1]} - #{[2][0]} - #{[2].name} - #{first[0]} - #{[first][0]} - #{[\\]]} - #{first[\\]]}'; 
     331      var subject = [ 'zero', 'one', [ 'two-zero' ] ]; 
     332      subject[2].name = 'two-zero-name'; 
     333      subject.first = subject[2]; 
     334      subject[']'] = '\\'; 
     335      subject.first[']'] = 'first\\'; 
     336      assertEqual('zero', new Template('#{[0]}').evaluate(subject)); 
     337      assertEqual('one', new Template('#{[1]}').evaluate(subject)); 
     338      assertEqual('two-zero', new Template('#{[2][0]}').evaluate(subject)); 
     339      assertEqual('two-zero-name', new Template('#{[2].name}').evaluate(subject)); 
     340      assertEqual('two-zero', new Template('#{first[0]}').evaluate(subject)); 
     341      assertEqual('\\', new Template('#{[\\]]}').evaluate(subject)); 
     342      assertEqual('first\\', new Template('#{first[\\]]}').evaluate(subject)); 
     343      assertEqual('empty - empty2', new Template('#{[]} - #{m[]}').evaluate({ '': 'empty', m: {'': 'empty2'}})); 
     344      assertEqual('zero = zero - one = one - two-zero - two-zero-name - two-zero - two-zero - \\ - first\\', new Template(source).evaluate(subject)); 
     345    }}, 
     346 
     347    testTemplateToTemplateReplacements: function() {with(this) { 
     348      var source = 'My name is #{name}, my job is #{job}'; 
     349      var subject = { 
     350        name: 'Stephan', 
     351        getJob: function() { return 'Web developer'; }, 
     352        toTemplateReplacements: function() { return { name: this.name, job: this.getJob() } } 
     353      }; 
     354      assertEqual('My name is Stephan, my job is Web developer', new Template(source).evaluate(subject)); 
     355    }}, 
     356 
     357    testTemplateEvaluationCombined: function() {with(this) { 
     358      var source = '#{name} is #{age} years old, managed by #{manager.name}, #{manager.age}.\n' + 
     359        'Colleagues include #{colleagues[0].name} and #{colleagues[1].name}.'; 
     360      var subject = { 
     361        name: 'Stephan', age: 22, 
     362        manager: { name: 'John', age: 29 }, 
     363        colleagues: [ { name: 'Mark' }, { name: 'Indy' } ] 
     364      }; 
     365      assertEqual('Stephan is 22 years old, managed by John, 29.\n' + 
     366        'Colleagues include Mark and Indy.', 
     367        new Template(source).evaluate(subject)); 
     368    }}, 
     369 
     370    testInterpolate: function() {with(this) { 
     371      var subject = { name: 'Stephan' }; 
     372      var pattern = /(^|.|\r|\n)(#\((.*?)\))/; 
     373      assertEqual('#{name}: Stephan', '\\#{name}: #{name}'.interpolate(subject)); 
     374      assertEqual('#(name): Stephan', '\\#(name): #(name)'.interpolate(subject, pattern)); 
     375    }}, 
     376 
    317377    testToQueryParams: function() {with(this) { 
    318378      // only the query part