├── DEMO.md ├── lib ├── ragtag.yml ├── ragtag │ ├── core_ext │ │ └── opvars.rb │ └── version.rb └── ragtag.rb ├── work ├── deprecated │ └── meta │ │ ├── name │ │ ├── title │ │ ├── version │ │ ├── created │ │ ├── requires │ │ ├── authors │ │ ├── homepage │ │ ├── summary │ │ ├── contact │ │ └── description ├── xmltailor │ ├── samples │ │ ├── example.rb │ │ ├── example_import.xtr │ │ ├── example-data.rb │ │ └── example.xtr │ ├── README │ └── lib │ │ └── xmltailor.rb └── rtals-old.rb ├── Gemfile ├── .yardopts ├── .gitignore ├── .travis.yml ├── MANIFEST ├── Assembly ├── demo ├── applique │ └── examples.rb ├── 01_syntax.md └── 02_example.md ├── INDEX.yml ├── HISTORY.md ├── try └── example.rb ├── LICENSE.txt ├── .index ├── README.md └── .gemspec /DEMO.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/ragtag.yml: -------------------------------------------------------------------------------- 1 | ../.index -------------------------------------------------------------------------------- /work/deprecated/meta/name: -------------------------------------------------------------------------------- 1 | rtals 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/title: -------------------------------------------------------------------------------- 1 | RTALS 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/version: -------------------------------------------------------------------------------- 1 | 0.5.0 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/created: -------------------------------------------------------------------------------- 1 | 2005-11-26 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/requires: -------------------------------------------------------------------------------- 1 | nokogiri 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gemspec 3 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --private 2 | --protected 3 | lib 4 | - 5 | [A-Z]*.* 6 | -------------------------------------------------------------------------------- /work/deprecated/meta/authors: -------------------------------------------------------------------------------- 1 | Thomas Sawyer 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/homepage: -------------------------------------------------------------------------------- 1 | http://rubyworks.github.com/rtals 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/summary: -------------------------------------------------------------------------------- 1 | Ruby Template Attribute Language 2 | -------------------------------------------------------------------------------- /work/deprecated/meta/contact: -------------------------------------------------------------------------------- 1 | rubyworks-mailinglist@googlegroups.com 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .reap/digest 2 | .yaropts 3 | doc 4 | log 5 | pkg 6 | tmp 7 | web 8 | -------------------------------------------------------------------------------- /work/xmltailor/samples/example.rb: -------------------------------------------------------------------------------- 1 | 2 | require 'facets/more/tailor' 3 | 4 | xt = Tailor.load('example.xtr') 5 | xt.process 6 | puts xt.xml_document 7 | 8 | -------------------------------------------------------------------------------- /work/deprecated/meta/description: -------------------------------------------------------------------------------- 1 | RTALS is a Ruby variation on Zope Page Templates and it's TAL specification. 2 | It differs from ZPT in that it is specifically geared for use by Ruby. 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | script: "bundle exec qed -Ilib" 4 | rvm: 5 | - 2.1.0 6 | - 2.0.0 7 | - 1.9.3 8 | - rbx 9 | - jruby 10 | matrix: 11 | allow_failures: 12 | - rvm: rbx 13 | cache: bundler 14 | 15 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast .index .yardopts bin lib man qed spec test *.md *.txt 2 | .index 3 | .yardopts 4 | lib/ragtag/core_ext/opvars.rb 5 | lib/ragtag/version.rb 6 | lib/ragtag.rb 7 | lib/ragtag.yml 8 | README.md 9 | HISTORY.md 10 | DEMO.md 11 | LICENSE.txt 12 | -------------------------------------------------------------------------------- /lib/ragtag/core_ext/opvars.rb: -------------------------------------------------------------------------------- 1 | class Binding 2 | 3 | # Returns the value of some variable. 4 | # 5 | # a = 2 6 | # binding["a"] #=> 2 7 | # 8 | def []( x ) 9 | eval( x.to_s ) 10 | end 11 | 12 | # Set the value of a local variable. 13 | # 14 | # binding["a"] = 4 15 | # a #=> 4 16 | # 17 | def []=( l, v ) 18 | eval( "lambda {|v| #{l} = v}").call( v ) 19 | end 20 | 21 | end 22 | 23 | -------------------------------------------------------------------------------- /lib/ragtag/version.rb: -------------------------------------------------------------------------------- 1 | class RagTag 2 | 3 | # Access to project metadata. 4 | def self.metadata 5 | @metadata ||= ( 6 | require 'yaml' 7 | YAML.load(File.new(File.dirname(__FILE__) + '/../ragtag.yml')) 8 | ) 9 | end 10 | 11 | # Access to project metadata as constants. 12 | def self.const_missing(name) 13 | key = name.to_s.downcase 14 | metadata[key] || super(name) 15 | end 16 | 17 | # TODO: This is only here b/c of bug in Ruby 1.8.x. 18 | VERSION = metadata['version'] 19 | 20 | end 21 | -------------------------------------------------------------------------------- /work/xmltailor/samples/example_import.xtr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 |

DUMDUM

15 | 16 |

DUMDUM

17 | 18 | 19 | Parent document title is DUMDUM 20 | 21 | 22 |
23 | 24 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | email: 3 | mailto: 4 | - ruby-talk@ruby-lang.org 5 | - rubyworks-mailinglist@googlegroups.com 6 | 7 | github: 8 | gh_pages: web 9 | 10 | gem: 11 | active: true 12 | 13 | qed: 14 | files: demo/*.rdoc 15 | priority: 2 16 | 17 | qedoc: 18 | files: demo/*.rdoc' 19 | title: RagTag Specs 20 | output: DEMO.rdoc 21 | 22 | syntax: 23 | files: lib/ 24 | 25 | dnote: 26 | title: Developer's Notes 27 | labels: ~ 28 | output: log/notes.html 29 | 30 | #locat: 31 | # active: false 32 | 33 | vclog: 34 | output: 35 | - log/changes.html 36 | - log/history.html 37 | 38 | -------------------------------------------------------------------------------- /demo/applique/examples.rb: -------------------------------------------------------------------------------- 1 | require 'ae' 2 | require 'ostruct' 3 | 4 | When /Given a template/ do |text| 5 | @template = text.strip 6 | end 7 | 8 | When /given a local binding/ do |code| 9 | @code = code 10 | end 11 | 12 | When /The result will be/ do |text| 13 | b = binding 14 | Kernel.eval(@code, b) 15 | result = RagTag.compile(@template, b).to_xhtml 16 | # IMPORTANT! We remove the new lines only b/c JRuby inserts extras. 17 | # Don't know why, but there you are. 18 | text = text.strip.delete("\n") 19 | result = result.strip.delete("\n") 20 | text.assert == result 21 | end 22 | 23 | -------------------------------------------------------------------------------- /INDEX.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 3 | ragtag 4 | 5 | version: 6 | 0.7.0 7 | 8 | title: 9 | RagTag 10 | 11 | summary: 12 | Ruby Tag Attribute Language 13 | 14 | description: 15 | RagTag is a Ruby variation on Zope Page Templates and it's 16 | TAL specification. It primarily differs from ZPT in that 17 | it is specifically geared for use with Ruby. 18 | 19 | requirements: 20 | - nokogiri 21 | - detroit (build) 22 | - qed (test) 23 | - ae (test) 24 | 25 | resources: 26 | home: http://rubyworks.github.com/ragtag 27 | code: http://github.com/rubyworks/ragtag 28 | bugs: http://github.com/rubyworks/ragtag/issues 29 | 30 | repositories: 31 | upstream: git://github.com/rubyworks/ragtag.git 32 | 33 | authors: 34 | - trans 35 | 36 | organizations: 37 | - Rubyworks 38 | 39 | created: 40 | 2005-11-26 41 | 42 | copyrights: 43 | - (c) 2005 RubyWorks (BSD-2-Clause) 44 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # RELEASE HISTORY 2 | 3 | ## 0.7.0 4 | 5 | This release ditches support for Ruby versions older than 2.0. 6 | 7 | Changes: 8 | 9 | * Remove support for Ruby older than 2.0. 10 | 11 | 12 | ## 0.6.1 | 2011-10-22 13 | 14 | This is just an administrative release to update the build 15 | configuration. Functionality of library hasn't changed. 16 | Also, the project is not distributed under the BSD-2-Clause 17 | license, rather than the Apache 2.0 license. 18 | 19 | Changes: 20 | 21 | * Switch licenses to BSD-2-Clause. 22 | * Modernize build configuration. 23 | 24 | 25 | ## 0.6.0 | 2010-10-15 26 | 27 | RTALS has been renamed to RagTag! 28 | 29 | Along with the name change, RagTag no longer uses namespaces to separate 30 | attribute names. 31 | 32 | Changes: 33 | 34 | * Rename project to RagTag. 35 | * No longer use XML namespace. 36 | 37 | 38 | ## 0.5.0 | 2010-04-19 39 | 40 | This is the first public release of RTALS. The library is 41 | finally at a point where I think it is generally usable. 42 | While it still has much room for improvement, all the 43 | essentially functionality is in place. 44 | 45 | Changes: 46 | 47 | * Happy 1st Release Day! 48 | -------------------------------------------------------------------------------- /work/xmltailor/samples/example-data.rb: -------------------------------------------------------------------------------- 1 | # 2 | 3 | title = "Hello Tom" 4 | 5 | spin = [ 'first', 'second', 'third' ] 6 | 7 | sub_spin = [ 'A', 'B', 'C', 'D', 8 | 'A', 'B', 'C', 'D', 9 | 'A', 'B', 'C', 'D', 10 | 'A', 'B', 'C', 'D', 11 | 'A', 'B', 'C', 'D', 12 | 'A', 'B', 'C', 'D', 13 | 'A', 'B', 'C', 'D', 14 | 'A', 'B', 'C', 'D', 15 | 'A', 'B', 'C', 'D', 16 | 'A', 'B', 'C', 'D', 17 | 'A', 'B', 'C', 'D', 18 | 'A', 'B', 'C', 'D', 19 | 'A', 'B', 'C', 'D', 20 | 'A', 'B', 'C', 'D', 21 | 'A', 'B', 'C', 'D', 22 | 'A', 'B', 'C', 'D', 23 | 'A', 'B', 'C', 'D', 24 | 'A', 'B', 'C', 'D', 25 | 'A', 'B', 'C', 'D', 26 | 'A', 'B', 'C', 'D', 27 | 'A', 'B', 'C', 'D', 28 | 'A', 'B', 'C', 'D', 29 | 'A', 'B', 'C', 'D', 30 | 'A', 'B', 'C', 'D', 31 | 'A', 'B', 'C', 'D', 32 | 'A', 'B', 'C', 'D', 33 | 'A', 'B', 'C', 'D', 34 | 'A', 'B', 'C', 'D', 35 | 'A', 'B', 'C', 'D', 36 | 'A', 'B', 'C', 'D', 37 | 'A', 'B', 'C', 'D', 38 | 'A', 'B', 'C', 'D', 39 | 'A', 'B', 'C', 'D' 40 | ] 41 | 42 | spin_again = [ [ 'A', 'Z' ], [ 'B', 'Y' ] ] 43 | 44 | thash = { 'a' => '1', 'b' => '2' } 45 | 46 | color = 'blue' 47 | 48 | condit = true 49 | 50 | class Tester 51 | 52 | attr_reader :punk 53 | 54 | def initialize 55 | @punk = "Yep" 56 | end 57 | 58 | end 59 | -------------------------------------------------------------------------------- /try/example.rb: -------------------------------------------------------------------------------- 1 | require 'ragtag' 2 | 3 | template = %q{ 4 | 5 | 6 |
7 |

John Doe

8 | 9 |

The customer is year old.

10 | 11 |
    12 |
  • Tag
  • 13 |
14 | 15 |
    16 |
  • 17 | 18 |
  • 19 |
20 | 21 | 22 | The customer is a senior citizen. 23 | 24 | 25 |
26 | The customer is a senior citizen. 27 |
28 |
29 | 30 | 31 | } 32 | 33 | require 'ostruct' 34 | 35 | customer = OpenStruct.new 36 | customer.name = "John Roberts" 37 | customer.address = "555 Hobart St" 38 | customer.city = "Palm Bay" 39 | customer.state = "FL" 40 | customer.zip = "32709" 41 | customer.age = 65 42 | customer.tags = [ 'ruby', 'javascript' ] 43 | 44 | customer_class = customer.age > 60 ? 'senior' : 'normal' 45 | 46 | 47 | xml = RagTag.compile(template, binding) 48 | 49 | puts xml 50 | 51 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | (BSD-2-Clause) 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 14 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 15 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 16 | COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 22 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | -------------------------------------------------------------------------------- /.index: -------------------------------------------------------------------------------- 1 | --- 2 | revision: 2013 3 | type: ruby 4 | sources: 5 | - INDEX.yml 6 | authors: 7 | - name: trans 8 | email: transfire@gmail.com 9 | organizations: 10 | - name: Rubyworks 11 | requirements: 12 | - name: nokogiri 13 | - groups: 14 | - build 15 | development: true 16 | name: detroit 17 | - groups: 18 | - test 19 | development: true 20 | name: qed 21 | - groups: 22 | - test 23 | development: true 24 | name: ae 25 | conflicts: [] 26 | alternatives: [] 27 | resources: 28 | - type: home 29 | uri: http://rubyworks.github.com/ragtag 30 | label: Homepage 31 | - type: code 32 | uri: http://github.com/rubyworks/ragtag 33 | label: Source Code 34 | - type: bugs 35 | uri: http://github.com/rubyworks/ragtag/issues 36 | label: Issue Tracker 37 | repositories: 38 | - name: upstream 39 | scm: git 40 | uri: git://github.com/rubyworks/ragtag.git 41 | categories: [] 42 | copyrights: 43 | - holder: RubyWorks 44 | year: '2005' 45 | license: BSD-2-Clause 46 | customs: [] 47 | paths: 48 | lib: 49 | - lib 50 | name: ragtag 51 | title: RagTag 52 | version: 0.7.0 53 | summary: Ruby Tag Attribute Language 54 | description: RagTag is a Ruby variation on Zope Page Templates and it's TAL specification. 55 | It primarily differs from ZPT in that it is specifically geared for use with Ruby. 56 | created: '2005-11-26' 57 | date: '2015-03-28' 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [RagTag](https://rubyworks.github.io/ragtag) 2 | 3 | [![Version](https://img.shields.io/gem/v/ragtag.svg)](https://rubygems.org/gems/ragtag) 4 | [![Build Status](https://secure.travis-ci.org/rubyworks/ragtag.png)](http://travis-ci.org/rubyworks/ragtag) 5 | [![Issues](https://img.shields.io/github/issues-raw/rubyworks/ragtag.svg)](https://github.com/rubyworks/ragtag/issues) 6 | 7 | RagTag is a Ruby-based template attribute lanaguage (TAL). 8 | Yea, it's an acronym. Don't blame me, blame those Zope people! :wink: 9 | 10 | RagTag is only loosely based on the Zope TAL sepcification. Its first priority 11 | is to *Ruby*, providing a good attribute based template system specially catered 12 | to it. 13 | 14 | 15 | ## HOW TO USE 16 | 17 | For the moment please see the [Wiki](http://wiki.github.com/rubyworks/ragtag/). 18 | 19 | 20 | ## HOW TO INSTALL 21 | 22 | Yea you know the procedure: 23 | 24 | $ gem install ragtag 25 | 26 | Using Bundler add the following to you gem file. 27 | 28 | gem 'ragtag' 29 | 30 | 31 | ## DEVELOPMENT 32 | 33 | We use [GitHub](https://github.com/rubyworks/ragtag) to host the project. 34 | 35 | We use [Detroit](https://detroit.github.io) to manage the build. 36 | 37 | 38 | ## COPYRIGHT 39 | 40 | Copyright (c) 2005 Rubyworks (BSD-2-Clause License) 41 | 42 | RagTag is distributable in accordance to the terms of the *FreeBSD* license. 43 | 44 | See LICENSE.txt file for details. 45 | -------------------------------------------------------------------------------- /work/xmltailor/README: -------------------------------------------------------------------------------- 1 | = xml:Tailor - XML Tag Attribute Language for Ruby 2 | 3 | Copyright (c) 2002 Thomas Sawyer, LGPL 4 | 5 | == Introduction 6 | 7 | xml:Tailor is a TAL, a Tag Attribute Language, for Ruby. What is a TAL? 8 | TALs are like template systems for SGML derivitives (XML/XHTML), 9 | but more powerful, and rather compare to ERuby. 10 | The advantage of a TALs, though, 11 | is that you can use standard visual HTML editors without difficulty. 12 | This is due to all dynamic markup is specified in attributes 13 | rather than as special tags. 14 | 15 | == Installation 16 | 17 | To install xml:Tailor simply unpack the gzipped tarball into your local site_ruby path. 18 | This path is usually +/usr/local/lib/site_ruby/1.6/+. 19 | An install script is not currently provided. 20 | 21 | == Requirements 22 | 23 | REXML 2.3.5+| 24 | TomsLib::REREXML 25 | 26 | == Usage 27 | 28 | Currently you'll just have to weed through the example and code. 29 | 30 | == Authentication 31 | 32 | Package:: xml:Tailor 33 | Author:: Thomas Sawyer 34 | Requires:: Ruby 1.6.7+ 35 | License:: Copyright (c) 2002 Thomas Sawyer, LGPL 36 | 37 | == Copy Notice 38 | 39 | xml:Tailor is free software; you can redistribute it and/or modify 40 | it under the terms of the GNU Lesser General Public License as published by 41 | the Free Software Foundation; either version 2 of the License, or 42 | (at your option) any later version. 43 | 44 | xml:Tailor is distributed in the hope that it will be useful, 45 | but WITHOUT ANY WARRANTY; without even the implied warranty of 46 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 47 | GNU Lesser General Public License for more details. 48 | 49 | You should have received a copy of the GNU Lesser General Public License 50 | along with xml:Tailor; if not, write to the Free Software 51 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 52 | 53 | -------------------------------------------------------------------------------- /demo/01_syntax.md: -------------------------------------------------------------------------------- 1 | # RagTag Syntax 2 | 3 | To demonstrate the RagTag syntax we first need to load the library. 4 | 5 | require 'ragtag' 6 | 7 | 8 | ## Content Rendering 9 | 10 | Tag content can be rendered using the `content` attribute. 11 | 12 | Given a template: 13 | 14 |

John Doe

15 | 16 | And given a local binding: 17 | 18 | name = 'Bill Hickcock' 19 | 20 | Then result will be: 21 | 22 |

Bill Hickcock

23 | 24 | 25 | ## Replacement 26 | 27 | A tag can be fully replaced by a rendering using the `replace` 28 | attribute. 29 | 30 | Given a template: 31 | 32 |

The customer is years old.

33 | 34 | And given a local binding: 35 | 36 | age = 40 37 | 38 | The result will be: 39 | 40 |

The customer is 40 years old.

41 | 42 | 43 | ## Atrtibute Rendering 44 | 45 | To render variable attributes use the `attr` attribute. 46 | 47 | Given a template: 48 | 49 |
50 | 51 | And given a local binding: 52 | 53 | sample = 'impressive' 54 | 55 | The result will be: 56 | 57 |
58 | 59 | 60 | ## Conditions 61 | 62 | Conditional sections can be created using the `if` attribute. 63 | 64 | Given a template: 65 | 66 | 67 | The customer is a senior citizen. 68 | 69 | 70 | And given a local binding: 71 | 72 | age = 60 73 | 74 | The result will be: 75 | 76 | 77 | The customer is a senior citizen. 78 | 79 | 80 | 81 | ## Iteration 82 | 83 | Iterations can be acheived via the `each` attribute. 84 | 85 | Given a template: 86 | 87 |
    88 |
  • Tag
  • 89 |
90 | 91 | And given a local binding: 92 | 93 | tags = ['ruby', 'perl', 'lua'] 94 | 95 | The result will be: 96 | 97 |
    98 |
  • ruby
  • 99 | 100 |
  • perl
  • 101 | 102 |
  • lua
  • 103 |
104 | 105 | -------------------------------------------------------------------------------- /demo/02_example.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | Here is a large example. 4 | 5 | Given a template: 6 | 7 | 8 | 9 |
10 |

John Doe

11 | 12 |

The customer is year old.

13 | 14 |
    15 |
  • Tag
  • 16 |
17 | 18 |
    19 |
  • 20 | 21 |
  • 22 |
23 | 24 | 25 | The customer is a senior citizen. 26 | 27 | 28 |
29 | The customer is a senior citizen. 30 |
31 |
32 | 33 | 34 | 35 | And given a local binding: 36 | 37 | customer = OpenStruct.new 38 | customer.name = "John Roberts" 39 | customer.address = "555 Hobart St" 40 | customer.city = "Palm Bay" 41 | customer.state = "FL" 42 | customer.zip = "32709" 43 | customer.age = 65 44 | customer.tags = [ 'ruby', 'javascript' ] 45 | 46 | customer_class = customer.age > 60 ? 'senior' : 'normal' 47 | 48 | Then result will be: 49 | 50 | 51 | 52 | 53 |
54 |

John Roberts

55 | 56 |

The customer is 65 year old.

57 | 58 |
    59 |
  • ruby
  • 60 | 61 |
  • javascript
  • 62 |
63 | 64 |
    65 |
  • 66 | r 67 | 68 | u 69 | 70 | b 71 | 72 | y 73 |
  • 74 | 75 |
  • 76 | j 77 | 78 | a 79 | 80 | v 81 | 82 | a 83 | 84 | s 85 | 86 | c 87 | 88 | r 89 | 90 | i 91 | 92 | p 93 | 94 | t 95 |
  • 96 |
97 | 98 | 99 | The customer is a senior citizen. 100 | 101 | 102 | 103 | The customer is a senior citizen. 104 | 105 |
106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /work/xmltailor/samples/example.xtr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Test 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |

Punk

24 |
25 | 26 |

Greeting

27 | 28 | Kill Me! 29 | 30 | Punk 31 | 32 | Punk 33 | 34 | 35 | This one is Dummy Spin - Dummy Spin 36 | 37 | 38 | 39 | This one is Dummy Spin 40 | 41 | 42 | 43 | This one is Dummy THash Key. Dummy THash Key = Dummy THash Key 44 | 45 | 46 | 47 | 48 | Loop in a loop Dummy Spin: 49 | Dummy Spin x Dummy Sub Spin 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |

Punk

61 |
62 | 63 |

Greeting

64 | 65 | Kill Me! 66 | 67 | Punk 68 | 69 | Punk 70 | 71 | 72 | This one is Dummy Spin - Dummy Spin 73 | 74 | 75 | 76 | This one is Dummy Spin 77 | 78 | 79 | 80 | This one is Dummy THash Key. Dummy THash Key = Dummy THash Key 81 | 82 | 83 | 84 | 85 | Loop in a loop Dummy Spin: 86 | Dummy Spin x Dummy Sub Spin 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /work/rtals-old.rb: -------------------------------------------------------------------------------- 1 | # = rtals.rb 2 | # 3 | # == Copyright (c) 2006 Thomas Sawyer 4 | # 5 | # Ruby License 6 | # 7 | # This module is free software. You may use, modify, and/or redistribute this 8 | # software under the same terms as Ruby. 9 | # 10 | # This program is distributed in the hope that it will be useful, but WITHOUT 11 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 | # FOR A PARTICULAR PURPOSE. 13 | # 14 | # == Author(s) 15 | # 16 | # * Thomas Sawyer 17 | 18 | # Author:: Thomas Sawyer 19 | # Copyright:: Copyright (c) 2006 Thomas Sawyer 20 | # License:: Ruby License 21 | 22 | require 'rexml/document' 23 | 24 | # = Tag Attribute Language for Ruby 25 | # 26 | # RubyTals is a Ruby variation on Zope Page Templates and it's TAL specification. 27 | # It differs from TAL in that it is specifically geared for use by Ruby. 28 | # 29 | # == Usage 30 | # 31 | # s = %q{ 32 | # 33 | # 34 | #

[X]

35 | #
36 | # [ANIMAL] 37 | #
38 | #
39 | # There are [ANIMAL SIZE] animals. 40 | #
41 | # 42 | # 43 | # } 44 | # 45 | # x = 'Our Little Zoo' 46 | # animal = ['Zebra', 'Monkey', 'Tiger' ] 47 | # out = '' 48 | # 49 | # prg = RubyTals.compile( s ) 50 | # puts eval(prg) 51 | # 52 | # == Note 53 | # 54 | # WARNING! This library is only minimally functional at this point. 55 | # If you would like to use it please consider improving upon it! 56 | # 57 | # Presently rTAL clauses can run arbitraty Ruby code. Although 58 | # upping the safety level before executing a compiled template 59 | # should be sufficiently protective in most cases, perhaps it would 60 | # be better to limit valid expressions to single object references, 61 | # ie. 'this.that', and then use a substitution of '.' for '/'. 62 | # Not only would this be highly protective, it would also be more 63 | # compatible with the original TAL spec; albiet this isn't exacty 64 | # how TALs interprets the '/' divider. 65 | # 66 | # On the other hand perhaps it is too much restraint. For instance 67 | # it would require the if-clause in the above exmaple to be 68 | # something like: 69 | # 70 | #
71 | # 72 | # and have a definition in the evaluateing code: 73 | # 74 | # def animal.plenty 75 | # size > 1 76 | # end 77 | # 78 | # It's a classic Saftey vs. Usability trade-off. Something to 79 | # consider for the future. 80 | 81 | module RubyTals 82 | 83 | def self.compile( xmlstr ) 84 | rxml = REXML::Document.new( xmlstr.strip ) 85 | "out=''\n" + parse( rxml ) + "\nout" 86 | end 87 | 88 | def self.execute( script, data ) 89 | vars.each_pair { |k,v| 90 | } 91 | eval script 92 | end 93 | 94 | def self.parse( xml ) 95 | building = '' 96 | body = [] 97 | 98 | xml.each do |elem| 99 | #p elem.class 100 | 101 | tag, mode = [], nil 102 | case elem 103 | when REXML::Element 104 | 105 | attributes, ruby_attributes = {}, {} 106 | elem.attributes.each { |k, v| 107 | if k =~ /^rtal:/i 108 | ruby_attributes[k.sub(/^rtal:/i,'')] = v 109 | else 110 | attributes[k] = v 111 | end 112 | } 113 | 114 | if ruby_attributes.empty? 115 | tag = add_tag( elem.name, parse( elem ), attributes ) 116 | 117 | else 118 | if bd = ruby_attributes["content"] 119 | tag << "out << #{bd}.to_s" 120 | elsif bd = ruby_attributes["replace"] 121 | tag << "out << #{bd}.to_s" 122 | mode = :replace 123 | else 124 | tag << parse( elem ) 125 | end 126 | 127 | if cond = ( ruby_attributes["if"] || ruby_attributes["condition"] ) 128 | #mode = :replace 129 | tag = ([ "if #{cond}" ].concat( tag ) << "end") 130 | end 131 | 132 | if enum = ( ruby_attributes["each"] || ruby_attributes["repeat"] ) 133 | loopf = "#{enum}.each do" 134 | if d = ruby_attributes["do"] 135 | loopf << " |#{d}|" 136 | end 137 | tag = ([ loopf ].concat( tag ) << "end") 138 | end 139 | 140 | unless mode == :replace #unless attributes.empty? # 141 | tag = add_tag( elem.name, tag, attributes ) 142 | end 143 | 144 | end 145 | 146 | else 147 | tag << "out << #{elem.to_s.inspect}" 148 | 149 | end 150 | 151 | body.concat tag 152 | end 153 | 154 | building << body.flatten.join("\n") 155 | return building 156 | end 157 | 158 | def self.add_tag( name, body, attributes={} ) 159 | b = [] 160 | if attributes.empty? 161 | b << "out << '<#{name}>'" 162 | b << body 163 | b << "out << ''" 164 | else 165 | s = '' 166 | s << "out << '<#{name} " 167 | s << attributes.collect { |k,v| %{#{k}="#{v}"} }.join(' ') 168 | s << ">'" 169 | b << s 170 | b << body 171 | b << "out << ''" 172 | end 173 | b 174 | end 175 | 176 | def self.loop_structure 177 | 178 | end 179 | 180 | end 181 | 182 | 183 | 184 | 185 | =begin 186 | 187 | s = %q{ 188 | 189 | 190 | This is a test. 191 |

[X]

192 |
193 | [ANIMAL] 194 |
195 |
196 | [ANIMAL SIZE] 197 |
198 | 199 | 200 | } 201 | 202 | x = '10' # problem with numbers 203 | animal = ['Zebra', 'Monkey', 'Tiger' ] 204 | out = '' 205 | 206 | prg = RubyTals.compile( s ) 207 | puts 208 | puts prg 209 | puts 210 | puts eval(prg) 211 | 212 | =end 213 | -------------------------------------------------------------------------------- /lib/ragtag.rb: -------------------------------------------------------------------------------- 1 | require 'nokogiri' 2 | require 'ragtag/version' 3 | require 'ragtag/core_ext/opvars' 4 | 5 | # = RAGTAG - A Tag Attribute Language for Ruby 6 | # 7 | # RubyTals is a Ruby variation on Zope Page Templates and it's TAL specification. 8 | # It differs from TAL in that it is specifically geared for use by Ruby. 9 | # 10 | # == Usage 11 | # 12 | # s = %q{ 13 | # 14 | # 15 | #

[X]

16 | #
17 | # [ANIMAL] 18 | #
19 | #
20 | # There are [ANIMAL SIZE] animals. 21 | #
22 | # 23 | # 24 | # } 25 | # 26 | # x = 'Our Little Zoo' 27 | # animal = ['Zebra', 'Monkey', 'Tiger' ] 28 | # 29 | # puts Ragtag.compile(s, binding) 30 | # 31 | # == Note 32 | # 33 | # Presently RagTag clauses can run arbitraty Ruby code. Although 34 | # upping the safety level before executing a compiled template 35 | # should be sufficiently protective in most cases, perhaps it would 36 | # be better to limit valid expressions to single object references, 37 | # ie. 'this.that', and then use a substitution of '.' for '/'. 38 | # Not only would this be highly protective, it would also be more 39 | # compatible with the original TAL spec; albeit this isn't exacty 40 | # how TALs interprets the '/' divider. 41 | # 42 | # On the other hand perhaps it is too much restraint. For instance 43 | # it would require the if-clause in the above exmaple to be 44 | # something like: 45 | # 46 | #
47 | # 48 | # and have a definition in the evaluating code: 49 | # 50 | # def animal.plenty 51 | # size > 1 52 | # end 53 | # 54 | # It's a classic Saftey vs. Usability trade-off. Something to 55 | # consider for the future. 56 | 57 | class RagTag 58 | 59 | # 60 | def self.compile(xml, scope=nil) 61 | new(xml).compile(scope) 62 | end 63 | 64 | #def self.execute( script, data ) 65 | # vars.each_pair { |k,v| 66 | # } 67 | # eval script 68 | #end 69 | 70 | # 71 | attr :xml 72 | 73 | # 74 | attr :scope 75 | 76 | # 77 | def initialize(xml) 78 | case xml 79 | when String 80 | @xml = Nokogiri::XML(xml) 81 | else 82 | @xml = xml 83 | end 84 | end 85 | 86 | # 87 | def compile(scope=nil) 88 | scope = scope || $TOPLEVEL_BINDING 89 | parse(@xml.root, scope) 90 | xml 91 | end 92 | 93 | #def parse_all(node) 94 | # while node 95 | # parse(node) 96 | # node = node.next 97 | # end 98 | #end 99 | 100 | #$rtals_each_stack = [] 101 | 102 | # 103 | def parse(node, scope) 104 | case node 105 | when Nokogiri::XML::Text 106 | # nothing 107 | when Nokogiri::XML::NodeSet 108 | parse_nodeset(node, scope) 109 | when Nokogiri::XML::Element 110 | if value = node['define'] 111 | eval(value, scope) 112 | end 113 | 114 | if node['if'] 115 | parse_if(node, scope) 116 | #elsif node['condition'] 117 | # parse_condition(node, scope) 118 | end 119 | 120 | if node['content'] 121 | parse_content(node, scope) 122 | elsif node['replace'] 123 | parse_replace(node, scope) 124 | end 125 | 126 | if node['attr'] || node['attributes'] 127 | parse_attributes(node, scope) 128 | end 129 | 130 | if node['each'] 131 | parse_each(node, scope) 132 | return 133 | elsif node['repeat'] 134 | parse_repeat(node, scope) 135 | return 136 | end 137 | 138 | node.children.each do |child| 139 | parse(child, scope) 140 | end 141 | 142 | if node['omit'] && node['omit'] != 'false' 143 | parse_omit(node, scope) 144 | end 145 | else 146 | raise node.inspect 147 | end 148 | return node 149 | end 150 | 151 | # 152 | def parse_nodeset(nodeset, scope) 153 | nodeset.each do |node| 154 | parse(node, scope) 155 | end 156 | nodeset 157 | end 158 | 159 | # 160 | def parse_content(node, scope) 161 | value = node['content'] 162 | node.content = eval(value, scope) 163 | node.remove_attribute('content') 164 | end 165 | 166 | # 167 | def parse_replace(node, scope) 168 | value = node['replace'] 169 | node.before(eval(value, scope).to_s) 170 | node.unlink 171 | end 172 | 173 | # 174 | def parse_attributes(node, scope) 175 | if attrs = node['attr'] 176 | assoc = attrs.split(',').map{ |e| e.strip.split(':') } 177 | assoc.each do |(k,v)| 178 | node[k] = eval(v, scope).to_s 179 | end 180 | node.remove_attribute('attr') 181 | end 182 | if attrs = node['attributes'] 183 | assoc = attrs.split(',').map{ |e| e.strip.split(':') } 184 | assoc.each do |(k,v)| 185 | node[k] = eval(v, scope).to_s 186 | end 187 | node.remove_attribute('attributes') 188 | end 189 | node 190 | end 191 | 192 | # 193 | def parse_if(node, scope) 194 | value = node['if'] 195 | if eval(value, scope) 196 | node.remove_attribute('if') 197 | parse(node.children, scope) 198 | else 199 | node.unlink 200 | end 201 | node 202 | end 203 | 204 | ## Like #parse_if but does not keep the conditional node. 205 | #def parse_condition(node, scope) 206 | # value = node['condition'] 207 | # if eval(value, scope) 208 | # parse(node.children, scope).each do |x| 209 | # x.unlink 210 | # node.add_previous_sibling(x) 211 | # end 212 | # node.unlink 213 | # else 214 | # node.unlink 215 | # end 216 | #end 217 | 218 | # 219 | def parse_each(node, scope) 220 | value = node['each'] 221 | args = node['do'] || 'x' 222 | copy = node.dup 223 | node.children.remove 224 | bindings = eval("#{value}.map{ |#{args}| binding }", scope) 225 | bindings.each do |each_scope| 226 | sect = parse(copy.dup.children, each_scope) 227 | sect.each do |x| 228 | node << x 229 | end 230 | end 231 | node.remove_attribute('each') 232 | node.remove_attribute('do') 233 | value 234 | end 235 | 236 | # 237 | def parse_repeat(node, scope) 238 | value = node['repeat'] 239 | args = node['do'] || 'x' 240 | copy = node.dup 241 | copy.remove_attribute('repeat') 242 | copy.remove_attribute('do') 243 | bindings = eval("#{value}.map{ |#{args}| binding }", scope) 244 | bindings.each do |each_scope| 245 | sect = parse(copy.dup, each_scope) 246 | node.add_previous_sibling(sect) 247 | # parse_omit(sect, scope) if sect['omit'] && sect['omit'] != 'false' 248 | end 249 | node.unlink 250 | value 251 | end 252 | 253 | # 254 | def parse_omit(node, scope) 255 | #parse(node.children, scope).each do |x| 256 | node.children.each do |x| 257 | x.unlink 258 | node.add_previous_sibling(x) 259 | end 260 | node.unlink 261 | end 262 | 263 | end 264 | 265 | -------------------------------------------------------------------------------- /work/xmltailor/lib/xmltailor.rb: -------------------------------------------------------------------------------- 1 | # XMLTailor/Ruby - XML Document Transformer for Ruby 2 | # An implementation of the XML Tag Attribute Insertion Language System (XMLTails) 3 | # Copyright (c) 2002 Thomas Sawyer, Ruby License 4 | 5 | # NEED TO CONSIDER THE SCOPE OF EVALS 6 | # SHOULD THEIR EVALUTATION ONLY BE WITHIN THE CONTEXT OF THEIR TAG OR GLOBAL, AS THEY ARE NOW? 7 | 8 | 9 | require 'tomslib/rerexml' 10 | require 'tomslib/filefetch' 11 | 12 | 13 | class Tailor 14 | 15 | include TomsLib::FileFetch 16 | 17 | attr_reader :xml_document 18 | 19 | # 20 | def initialize(xml) 21 | xml_string = fetch_xml(xml) 22 | @xml_document = REXML::Document.new(xml_string) 23 | load_ruby 24 | end 25 | 26 | 27 | # Loads the ruby processing instruction. 28 | def load_ruby 29 | ruby_pi = @xml_document.find_all { |i| i.is_a? REXML::Instruction and i.target == 'xml:script' } 30 | ruby_pi.each do |pi| 31 | if pi.attributes['uri'] == 'ruby' 32 | load_eval(fetch_xml(pi.attributes['url'])) 33 | end 34 | end 35 | end 36 | 37 | def load_eval(evaluate) 38 | eval(evaluate.untaint, TOPLEVEL_BINDING) 39 | end 40 | 41 | # 42 | def process 43 | 44 | # process imports 45 | process_import(@xml_document, TOPLEVEL_BINDING) 46 | 47 | # process each loops 48 | loops = REXML::XPath.match(@xml_document, '//*[@each]') 49 | loops.reverse! # reverse loops to do inner nested loops first 50 | loops.each do |element| 51 | # 52 | new_element_string = '' 53 | element.write(new_element_string) 54 | # 55 | loop_each = element.attributes['each'] 56 | if element.attributes.has_key?('do') 57 | loop_do = element.attributes['do'] 58 | else 59 | loop_do = 'item' 60 | end 61 | # collect bindings 62 | indexer = "#{loop_do.split(',')[0]}_index" 63 | loop_eval = "i = -1 \n" 64 | loop_eval += "#{loop_each}.collect do |#{loop_do}| \n" 65 | loop_eval += " #{indexer} = i += 1 \n" 66 | loop_eval += " binding \n" 67 | loop_eval += "end \n" 68 | loop_bindings = eval(loop_eval.untaint, TOPLEVEL_BINDING) 69 | loop_bindings.each do |loop_binding| # for each binding substitute items 70 | # make new element 71 | new_element_source = REXML::SourceFactory.create_from(new_element_string) 72 | new_element = REXML::Element.new(new_element_source) 73 | new_element.attributes.get_attribute('each').remove 74 | new_element.attributes.get_attribute('do').remove if element.attributes.has_key?('do') 75 | # process items 76 | process_eval(new_element.untaint, loop_binding) 77 | process_if(new_element, loop_binding) 78 | process_content(new_element, loop_binding) 79 | process_attributes_if(new_element, loop_binding) 80 | process_attributes(new_element, loop_binding) 81 | # insert the new element 82 | element.parent.insert_before(element, new_element) 83 | end 84 | element.remove 85 | end 86 | 87 | # those not contained in each loops 88 | process_eval(@xml_document, TOPLEVEL_BINDING) 89 | process_if(@xml_document, TOPLEVEL_BINDING) 90 | process_content(@xml_document, TOPLEVEL_BINDING) 91 | process_attributes_if(@xml_document, TOPLEVEL_BINDING) 92 | process_attributes(@xml_document, TOPLEVEL_BINDING) 93 | 94 | end 95 | 96 | 97 | # Process imports. 98 | def process_import(xml_context, eval_context) 99 | elements = REXML::XPath.match(xml_context, './/*[@import]') 100 | elements.each do |element| 101 | import = element.attributes['import'].strip 102 | xpath = element.attributes['xpath'].strip if element.attributes.has_key?('xpath') 103 | import_file, import_context = import.split('#') 104 | import_file.strip! 105 | import_context.strip! if import_context 106 | import_rxtal = Tailor.new(import_file) 107 | import_rxtal.process 108 | if import_context 109 | context_element = REXML::XPath.first(import_rxtal.xml_document, ".//*[@importable='#{import_context}']") 110 | if xpath 111 | import_nodes = REXML::XPath.match(context_element, xpath) 112 | else 113 | import_nodes = [context_element] 114 | end 115 | else 116 | if xpath 117 | import_nodes = REXML::XPath.match(import_rxtal.xml_document, xpath) 118 | else 119 | import_nodes = [import_rxtal.xml_document.root] 120 | end 121 | end 122 | import_nodes.each do |import_element| 123 | import_element.attributes.get_attribute('importable').remove if import_element.attributes.has_key?('importable') 124 | element.parent.insert_before(element, import_element) 125 | end 126 | element.remove 127 | end 128 | end 129 | 130 | 131 | # Process evaluations. 132 | def process_eval(xml_context, eval_context) 133 | elements = REXML::XPath.match(xml_context, './/*[@eval]') 134 | elements.each do |element| 135 | begin 136 | evaluate = element.attributes['eval'].strip 137 | eval(evaluate.untaint, eval_context) 138 | element.attributes.get_attribute('eval').remove 139 | rescue NameError 140 | next 141 | end 142 | end 143 | 144 | end 145 | 146 | 147 | # Process if conditions. 148 | def process_if(xml_context, eval_context) 149 | elements = REXML::XPath.match(xml_context, './/*[@if]') 150 | elements.each do |element| 151 | begin 152 | condition = element.attributes['if'].strip 153 | result = eval(condition.untaint, eval_context) 154 | if result 155 | element.attributes.get_attribute('if').remove 156 | else 157 | element.remove 158 | end 159 | rescue NameError 160 | next 161 | end 162 | end 163 | end 164 | 165 | 166 | # Process content substitutions. 167 | def process_content(xml_context, eval_context) 168 | elements = REXML::XPath.match(xml_context, './/*[@content]') 169 | elements.each do |element| 170 | begin 171 | content = element.attributes['content'].strip 172 | element.text = eval(content.untaint, eval_context).to_s 173 | element.attributes.get_attribute('content').remove 174 | rescue NameError 175 | next 176 | end 177 | end 178 | end 179 | 180 | 181 | # Process attributes if conditions. 182 | def process_attributes_if(xml_context, eval_context) 183 | elements = REXML::XPath.match(xml_context, './/*[@attributes_if]') 184 | elements.each do |element| 185 | begin 186 | assignments = element.attributes['attributes_if'].split('\\') 187 | assignments.each do |assignment| 188 | attribute_name, attribute_condition = assignment.split('=') 189 | attribute_name.strip! 190 | attribute_condition.strip! 191 | result = eval(attribute_condition.untaint, eval_context) 192 | if not result 193 | if element.attributes.has_key?(attribute_name) 194 | element.attributes.get_attribute(attribute_name).remove 195 | end 196 | end 197 | end 198 | element.attributes.get_attribute('attributes_if').remove 199 | rescue NameError 200 | next 201 | end 202 | end 203 | end 204 | 205 | 206 | # Process attributes substitutions. 207 | def process_attributes(xml_context, eval_context) 208 | 209 | elements = REXML::XPath.match(xml_context, './/*[@attributes]') 210 | elements.each do |element| 211 | begin 212 | assignments = element.attributes['attributes'].split('\\') 213 | assignments.each do |assignment| 214 | attribute_name, attribute_value = assignment.split('=') 215 | attribute_name.strip! 216 | attribute_value.strip! 217 | if element.attributes.has_key?(attribute_name) 218 | element.attributes[attribute_name] = eval(attribute_value.untaint, eval_context).to_s 219 | end 220 | end 221 | element.attributes.get_attribute('attributes').remove 222 | rescue NameError 223 | next 224 | end 225 | end 226 | end 227 | 228 | end 229 | -------------------------------------------------------------------------------- /.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | require 'pathname' 5 | 6 | module Indexer 7 | 8 | # Convert index data into a gemspec. 9 | # 10 | # Notes: 11 | # * Assumes all executables are in bin/. 12 | # * Does not yet handle default_executable setting. 13 | # * Does not yet handle platform setting. 14 | # * Does not yet handle required_ruby_version. 15 | # * Support for rdoc entries is weak. 16 | # 17 | class GemspecExporter 18 | 19 | # File globs to include in package --unless a manifest file exists. 20 | FILES = ".index .yardopts alt bin data demo ext features lib man spec test try* [A-Z]*.*" unless defined?(FILES) 21 | 22 | # File globs to omit from FILES. 23 | OMIT = "Config.rb" unless defined?(OMIT) 24 | 25 | # Standard file patterns. 26 | PATTERNS = { 27 | :root => '{.index,Gemfile}', 28 | :bin => 'bin/*', 29 | :lib => 'lib/{**/}*', #.rb', 30 | :ext => 'ext/{**/}extconf.rb', 31 | :doc => '*.{txt,rdoc,md,markdown,tt,textile}', 32 | :test => '{test,spec}/{**/}*.rb' 33 | } unless defined?(PATTERNS) 34 | 35 | # For which revision of indexer spec is this converter intended? 36 | REVISION = 2013 unless defined?(REVISION) 37 | 38 | # 39 | def self.gemspec 40 | new.to_gemspec 41 | end 42 | 43 | # 44 | attr :metadata 45 | 46 | # 47 | def initialize(metadata=nil) 48 | @root_check = false 49 | 50 | if metadata 51 | root_dir = metadata.delete(:root) 52 | if root_dir 53 | @root = root_dir 54 | @root_check = true 55 | end 56 | metadata = nil if metadata.empty? 57 | end 58 | 59 | @metadata = metadata || YAML.load_file(root + '.index') 60 | 61 | if @metadata['revision'].to_i != REVISION 62 | warn "This gemspec exporter was not designed for this revision of index metadata." 63 | end 64 | end 65 | 66 | # 67 | def has_root? 68 | root ? true : false 69 | end 70 | 71 | # 72 | def root 73 | return @root if @root || @root_check 74 | @root_check = true 75 | @root = find_root 76 | end 77 | 78 | # 79 | def manifest 80 | return nil unless root 81 | @manifest ||= Dir.glob(root + 'manifest{,.txt}', File::FNM_CASEFOLD).first 82 | end 83 | 84 | # 85 | def scm 86 | return nil unless root 87 | @scm ||= %w{git hg}.find{ |m| (root + ".#{m}").directory? }.to_sym 88 | end 89 | 90 | # 91 | def files 92 | return [] unless root 93 | @files ||= \ 94 | if manifest 95 | File.readlines(manifest). 96 | map{ |line| line.strip }. 97 | reject{ |line| line.empty? || line[0,1] == '#' } 98 | else 99 | list = [] 100 | Dir.chdir(root) do 101 | FILES.split(/\s+/).each do |pattern| 102 | list.concat(glob(pattern)) 103 | end 104 | OMIT.split(/\s+/).each do |pattern| 105 | list = list - glob(pattern) 106 | end 107 | end 108 | list 109 | end.select{ |path| File.file?(path) }.uniq 110 | end 111 | 112 | # 113 | def glob_files(pattern) 114 | return [] unless root 115 | Dir.chdir(root) do 116 | Dir.glob(pattern).select do |path| 117 | File.file?(path) && files.include?(path) 118 | end 119 | end 120 | end 121 | 122 | def patterns 123 | PATTERNS 124 | end 125 | 126 | def executables 127 | @executables ||= \ 128 | glob_files(patterns[:bin]).map do |path| 129 | File.basename(path) 130 | end 131 | end 132 | 133 | def extensions 134 | @extensions ||= \ 135 | glob_files(patterns[:ext]).map do |path| 136 | File.basename(path) 137 | end 138 | end 139 | 140 | def name 141 | metadata['name'] || metadata['title'].downcase.gsub(/\W+/,'_') 142 | end 143 | 144 | def homepage 145 | page = ( 146 | metadata['resources'].find{ |r| r['type'] =~ /^home/i } || 147 | metadata['resources'].find{ |r| r['name'] =~ /^home/i } || 148 | metadata['resources'].find{ |r| r['name'] =~ /^web/i } 149 | ) 150 | page ? page['uri'] : false 151 | end 152 | 153 | def licenses 154 | metadata['copyrights'].map{ |c| c['license'] }.compact 155 | end 156 | 157 | def require_paths 158 | paths = metadata['paths'] || {} 159 | paths['load'] || ['lib'] 160 | end 161 | 162 | # 163 | # Convert to gemnspec. 164 | # 165 | def to_gemspec 166 | if has_root? 167 | Gem::Specification.new do |gemspec| 168 | to_gemspec_data(gemspec) 169 | to_gemspec_paths(gemspec) 170 | end 171 | else 172 | Gem::Specification.new do |gemspec| 173 | to_gemspec_data(gemspec) 174 | to_gemspec_paths(gemspec) 175 | end 176 | end 177 | end 178 | 179 | # 180 | # Convert pure data settings. 181 | # 182 | def to_gemspec_data(gemspec) 183 | gemspec.name = name 184 | gemspec.version = metadata['version'] 185 | gemspec.summary = metadata['summary'] 186 | gemspec.description = metadata['description'] 187 | 188 | metadata['authors'].each do |author| 189 | gemspec.authors << author['name'] 190 | 191 | if author.has_key?('email') 192 | if gemspec.email 193 | gemspec.email << author['email'] 194 | else 195 | gemspec.email = [author['email']] 196 | end 197 | end 198 | end 199 | 200 | gemspec.licenses = licenses 201 | 202 | requirements = metadata['requirements'] || [] 203 | requirements.each do |req| 204 | next if req['optional'] 205 | next if req['external'] 206 | 207 | name = req['name'] 208 | groups = req['groups'] || [] 209 | 210 | version = gemify_version(req['version']) 211 | 212 | if groups.empty? or groups.include?('runtime') 213 | # populate runtime dependencies 214 | if gemspec.respond_to?(:add_runtime_dependency) 215 | gemspec.add_runtime_dependency(name,*version) 216 | else 217 | gemspec.add_dependency(name,*version) 218 | end 219 | else 220 | # populate development dependencies 221 | if gemspec.respond_to?(:add_development_dependency) 222 | gemspec.add_development_dependency(name,*version) 223 | else 224 | gemspec.add_dependency(name,*version) 225 | end 226 | end 227 | end 228 | 229 | # convert external dependencies into gemspec requirements 230 | requirements.each do |req| 231 | next unless req['external'] 232 | gemspec.requirements << ("%s-%s" % req.values_at('name', 'version')) 233 | end 234 | 235 | gemspec.homepage = homepage 236 | gemspec.require_paths = require_paths 237 | gemspec.post_install_message = metadata['install_message'] 238 | end 239 | 240 | # 241 | # Set gemspec settings that require a root directory path. 242 | # 243 | def to_gemspec_paths(gemspec) 244 | gemspec.files = files 245 | gemspec.extensions = extensions 246 | gemspec.executables = executables 247 | 248 | if Gem::VERSION < '1.7.' 249 | gemspec.default_executable = gemspec.executables.first 250 | end 251 | 252 | gemspec.test_files = glob_files(patterns[:test]) 253 | 254 | unless gemspec.files.include?('.document') 255 | gemspec.extra_rdoc_files = glob_files(patterns[:doc]) 256 | end 257 | end 258 | 259 | # 260 | # Return a copy of this file. This is used to generate a local 261 | # .gemspec file that can automatically read the index file. 262 | # 263 | def self.source_code 264 | File.read(__FILE__) 265 | end 266 | 267 | private 268 | 269 | def find_root 270 | root_files = patterns[:root] 271 | if Dir.glob(root_files).first 272 | Pathname.new(Dir.pwd) 273 | elsif Dir.glob("../#{root_files}").first 274 | Pathname.new(Dir.pwd).parent 275 | else 276 | #raise "Can't find root of project containing `#{root_files}'." 277 | warn "Can't find root of project containing `#{root_files}'." 278 | nil 279 | end 280 | end 281 | 282 | def glob(pattern) 283 | if File.directory?(pattern) 284 | Dir.glob(File.join(pattern, '**', '*')) 285 | else 286 | Dir.glob(pattern) 287 | end 288 | end 289 | 290 | def gemify_version(version) 291 | case version 292 | when /^(.*?)\+$/ 293 | ">= #{$1}" 294 | when /^(.*?)\-$/ 295 | "< #{$1}" 296 | when /^(.*?)\~$/ 297 | "~> #{$1}" 298 | else 299 | version 300 | end 301 | end 302 | 303 | end 304 | 305 | end 306 | 307 | Indexer::GemspecExporter.gemspec --------------------------------------------------------------------------------