txprails 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,191 @@
1
+ #include "ruby.h"
2
+ #include "txparse.h"
3
+
4
+ //---------------------------------------------------------------------------
5
+ const string TXPDictSpaces = " \t\r\n";
6
+ const string TXPDictWordLim = " \t\r\n,<>/=";
7
+ const string TXPDictSingleQuote = "'";
8
+ const string TXPDictDoubleQuote = "\"";
9
+
10
+ string txp_domain_str = "dmstk";
11
+
12
+ //---------------------------------------------------------------------------
13
+ static bool txp_isspace(string::const_iterator iter)
14
+ {
15
+ return find(TXPDictSpaces.begin(), TXPDictSpaces.end(), *iter) != TXPDictSpaces.end();
16
+ }
17
+
18
+ //---------------------------------------------------------------------------
19
+ static bool txp_valid_chars(
20
+ string::const_iterator iter,
21
+ string::const_iterator end
22
+ )
23
+ {
24
+ if (*iter == '/') { iter++; end++; }
25
+ for (; iter != end; ++iter) {
26
+ if (::isspace(*iter)) return false;
27
+ if (*iter == ':') return true;
28
+ if (!::isalpha(*iter)) return false;
29
+ }
30
+ return false; // No se encontró los dos puntos...
31
+ }
32
+
33
+ //---------------------------------------------------------------------------
34
+ string::const_iterator txp_find_tag(
35
+ string::const_iterator iter,
36
+ string::const_iterator end
37
+ )
38
+ {
39
+ // Encontramos un TAG de plantilla cuando tengamos "<" + [a-z]{3} + ":"
40
+ // O también cuando tengamos "</" ya que es de cierre...
41
+ while (iter != end) {
42
+ if ((*iter == '<') && (end - iter > 5) && txp_valid_chars(iter+1, iter+7))
43
+ return iter;
44
+ ++iter;
45
+ }
46
+ return iter;
47
+ }
48
+
49
+ //---------------------------------------------------------------------------
50
+ string::const_iterator txp_find_close(
51
+ string::const_iterator iter,
52
+ string::const_iterator end
53
+ )
54
+ {
55
+ // Encontramos un final de TAG cuando tengamos ">"
56
+ for (; iter != end; ++iter) {
57
+ if (*iter == '>') return iter+1;
58
+ }
59
+ return iter;
60
+ }
61
+
62
+ //---------------------------------------------------------------------------
63
+ string::const_iterator txp_find_par(
64
+ string::const_iterator begin,
65
+ string::const_iterator end,
66
+ VALUE tag
67
+ )
68
+ {
69
+ // TODO:
70
+ // Tenemos que hacer el mismo recorrido a partir de "begin" que en el
71
+ // bucle de parsing, pero esta vez sólo para encontrar un tag coincidente
72
+ // con el que nos pasan... Todo esto no es más que una prueba, habría que
73
+ // hacer un algoritmo más "robusto" para esto (Por ejemplo con pilas para
74
+ // encontrar un tag balanceado, ahora no pueden estar anidados.)
75
+
76
+ string::const_iterator old;
77
+
78
+ while (begin != end) {
79
+ begin = txp_find_tag(begin, end);
80
+ if (begin != end) {
81
+ // Estamos en un tag...
82
+ old = begin;
83
+ begin = txp_find_close(begin, end);
84
+ if (begin != end) {
85
+ VALUE tag_txt = rb_str_new2(string(old, begin).c_str());
86
+ VALUE new_tag = rb_class_new_instance(1, &tag_txt, rb_const_get(mTXParse, rb_intern("Tag")));
87
+ /*
88
+ printf(":::: SEARCHING PAR (D:'%s' vs '%s', N:'%s' vs. '%s')\n"
89
+ , RSTRING(rb_iv_get(tag, "@domain"))->ptr
90
+ , RSTRING(rb_iv_get(new_tag, "@domain"))->ptr
91
+ , RSTRING(rb_iv_get(tag, "@name"))->ptr
92
+ , RSTRING(rb_iv_get(new_tag, "@name"))->ptr
93
+ );
94
+ */
95
+
96
+ // Vale, comprobamos que el tag se llama igual...
97
+ if (!rb_str_cmp(rb_iv_get(tag, "@domain"), rb_iv_get(new_tag, "@domain")) &&
98
+ !rb_str_cmp(rb_iv_get(tag, "@name"), rb_iv_get(new_tag, "@name"))) {
99
+ // printf(":::: FOUND!!!\n");
100
+
101
+ // Es el mismo TAG... ¿Es de cierre?
102
+ if (rb_iv_get(new_tag, "@is_close") == Qtrue) {
103
+ // printf(":::: New Tag is CLOSE\n");
104
+ // printf(":::: DBG: [%s]\n", string(old, end).c_str());
105
+ return old;
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ return end; // What else?
112
+ }
113
+
114
+
115
+ #define DEBUG_POS(str, pos) \
116
+ {\
117
+ string strdebug = string(str.size(), ' '); \
118
+ strdebug[pos] = '$'; \
119
+ printf("|%s|\n", str.c_str()); \
120
+ printf("|%s|\n", strdebug.c_str()); \
121
+ }
122
+
123
+ //---------------------------------------------------------------------------
124
+ void txp_parse_tag(const string tag, string &domain, string &name, map<string, string> &attrs, bool &close)
125
+ {
126
+ string::size_type pos = 0, pos2;
127
+
128
+ if ((tag.size() == 0) || (tag[pos++] != '<')) {
129
+ rb_raise(rb_eSyntaxError, "Tag should begin with '<'");
130
+ // rb_raise no retorna...
131
+ }
132
+
133
+ if (tag[pos] == '/') {
134
+ close = true;
135
+ pos++;
136
+ }
137
+
138
+ pos2 = tag.find_first_of(":/> \t\r\n", pos);
139
+ if ((pos2 == string::npos) || (tag[pos2] != ':')) {
140
+ rb_warn("TAG Should have a 'domain'. Assuming 'dmstk'");
141
+ // rb_raise(rb_eSyntaxError, "Tag should have a domain.");
142
+ domain = "dmstk";
143
+ name = string(tag, pos, pos2 - pos);
144
+ } else {
145
+ domain = string(tag, pos, pos2 - pos);
146
+ pos = pos2 + 1;
147
+ pos2 = tag.find_first_of(":/> \t\r\n", pos);
148
+ name = string(tag, pos, pos2 - pos);
149
+ }
150
+
151
+ // Si soy tag de cierre, no debo tener attributos.
152
+ if (close) return;
153
+
154
+ // Parseado el dominio y el nombre, saltamos a los attributos...
155
+ pos = tag.find_first_not_of(" \t\r\n", pos2);
156
+ if ((pos == string::npos) || (pos == '>')) return;
157
+
158
+ while ((pos != string::npos) && (tag[pos] != '>')) {
159
+ // Caso especial del tag de autocierre y el final de tag '>'
160
+ if ((tag[pos] == '/') && (tag[pos+1] == '>')) {
161
+ close = true; // autoclose ??
162
+ pos = string::npos;
163
+ break;
164
+ }
165
+
166
+ pos2 = tag.find_first_of(" \t\r\n,<>/=", pos);
167
+ string attrname = string(tag, pos, pos2 - pos);
168
+ string attrval = "";
169
+
170
+ pos2 = tag.find_first_not_of(" \t\r\n", pos2);
171
+ if ((pos2 != string::npos) && (tag[pos2]=='=')) {
172
+ // Parseando valor de atributo
173
+ pos2 = tag.find_first_not_of(" \t\r\n", pos2+1);
174
+
175
+ switch (tag[pos2]) {
176
+ case '\'': pos = pos2+1; pos2 = tag.find_first_of("'", pos); break;
177
+ case '"': pos = pos2+1; pos2 = tag.find_first_of("\"", pos); break;
178
+ default: pos = pos2; pos2 = tag.find_first_of(" \t\r\n"); break;
179
+ }
180
+ attrval = string(tag, pos, pos2 - pos);
181
+ } else {
182
+ // Atributo sin valores!! Asumimos --> nombre="nombre"
183
+ attrval = attrname;
184
+ }
185
+
186
+ attrs[attrname] = attrval;
187
+
188
+ // Tenemos que dejar "pos" en el siguiente atributo...
189
+ pos = tag.find_first_not_of(" \t\r\n", pos2+1);
190
+ }
191
+ }
@@ -0,0 +1,17 @@
1
+ #ifndef TXPARSE_LIB
2
+ #define TXPARSE_LIB
3
+
4
+ #include <string>
5
+ #include <map>
6
+
7
+ using namespace std;
8
+
9
+ extern string txp_domain_str;
10
+
11
+ //---------------------------------------------------------------------------
12
+ string::const_iterator txp_find_tag(string::const_iterator iter, string::const_iterator end);
13
+ string::const_iterator txp_find_close(string::const_iterator iter, string::const_iterator end);
14
+ string::const_iterator txp_find_par(string::const_iterator begin, string::const_iterator end, VALUE tag);
15
+ void txp_parse_tag(const string tag, string &domain, string &name, map<string, string> &attrs, bool &close);
16
+
17
+ #endif
@@ -0,0 +1,109 @@
1
+ #include "ruby.h"
2
+ #include "txparse.h"
3
+
4
+ extern "C" VALUE tag_initialize(VALUE self, VALUE orig)
5
+ {
6
+ string domain, name;
7
+ map<string, string> attrs;
8
+ bool is_close = false;
9
+ VALUE attr_hash = rb_hash_new();
10
+
11
+ rb_check_type(orig, T_STRING);
12
+ txp_parse_tag(string(RSTRING(orig)->ptr), domain, name, attrs, is_close);
13
+
14
+ /*
15
+ printf("PARSE-TAG DBG: ORIG='%s'\n", RSTRING(orig)->ptr);
16
+ printf(" DOMAIN='%s', NAME='%s'\n", domain.c_str(), name.c_str());
17
+ printf(" ATTRS(num)='%d', CLOSED='%c'\n", (int)attrs.size(), is_close ? 'Y' : 'N');
18
+ */
19
+
20
+ if (attrs.size()) {
21
+ for (map<string, string>::const_iterator iter = attrs.begin()
22
+ ; iter != attrs.end()
23
+ ; ++iter)
24
+ {
25
+ rb_hash_aset(attr_hash, rb_str_new2((*iter).first.c_str()), rb_str_new2((*iter).second.c_str()));
26
+ }
27
+ }
28
+
29
+ rb_iv_set(self, "@name", rb_str_new2(name.c_str()));
30
+ rb_iv_set(self, "@domain", rb_str_new2(domain.c_str()));
31
+ rb_iv_set(self, "@is_close", is_close ? Qtrue : Qfalse);
32
+ rb_iv_set(self, "@attr", attr_hash);
33
+ rb_iv_set(self, "@orig", orig);
34
+ return self;
35
+ }
36
+
37
+ extern "C" VALUE tag_name(VALUE self)
38
+ {
39
+ return rb_iv_get(self, "@name");
40
+ }
41
+
42
+ extern "C" VALUE tag_name_set(VALUE self, VALUE new_name)
43
+ {
44
+ return rb_iv_set(self, "@name", new_name);
45
+ }
46
+
47
+
48
+ extern "C" VALUE tag_domain(VALUE self)
49
+ {
50
+ return rb_iv_get(self, "@domain");
51
+ }
52
+
53
+ extern "C" VALUE tag_domain_set(VALUE self, VALUE new_domain)
54
+ {
55
+ return rb_iv_set(self, "@domain", new_domain);
56
+ }
57
+
58
+ extern "C" VALUE tag_thing(VALUE self)
59
+ {
60
+ return rb_iv_get(self, "@thing");
61
+ }
62
+
63
+ extern "C" VALUE tag_thing_set(VALUE self, VALUE new_thing)
64
+ {
65
+ return rb_iv_set(self, "@thing", new_thing);
66
+ }
67
+
68
+ extern "C" VALUE tag_attributes(VALUE self)
69
+ {
70
+ return rb_iv_get(self, "@attr");
71
+ }
72
+
73
+
74
+
75
+ extern "C" VALUE tag_is_close(VALUE self)
76
+ {
77
+ return rb_iv_get(self, "@is_close");
78
+ }
79
+
80
+ extern "C" VALUE tag_orig(VALUE self)
81
+ {
82
+ return rb_iv_get(self, "@orig");
83
+ }
84
+
85
+
86
+
87
+
88
+ VALUE mTXParseTag = Qnil;
89
+ void Init_TXParseTag()
90
+ {
91
+ // VALUE TXParse_Module = rb_define_module("TXParse");
92
+ mTXParseTag = rb_define_class_under(mTXParse, "Tag", rb_cObject);
93
+
94
+ rb_define_method(mTXParseTag, "initialize", RUBY_METHOD_FUNC(tag_initialize), 1);
95
+
96
+ rb_define_method(mTXParseTag, "name", RUBY_METHOD_FUNC(tag_name), 0);
97
+ rb_define_method(mTXParseTag, "name=", RUBY_METHOD_FUNC(tag_name_set), 1);
98
+
99
+ rb_define_method(mTXParseTag, "domain", RUBY_METHOD_FUNC(tag_domain), 0);
100
+ rb_define_method(mTXParseTag, "domain=", RUBY_METHOD_FUNC(tag_domain_set), 1);
101
+
102
+ rb_define_method(mTXParseTag, "thing", RUBY_METHOD_FUNC(tag_thing), 0);
103
+ rb_define_method(mTXParseTag, "thing=", RUBY_METHOD_FUNC(tag_thing_set), 1);
104
+
105
+ rb_define_method(mTXParseTag, "attr", RUBY_METHOD_FUNC(tag_attributes), 0);
106
+
107
+ rb_define_method(mTXParseTag, "close?", RUBY_METHOD_FUNC(tag_is_close), 0);
108
+ rb_define_method(mTXParseTag, "orig", RUBY_METHOD_FUNC(tag_orig), 0);
109
+ }
@@ -0,0 +1,7 @@
1
+ #ifndef TXPARSE_TAG
2
+ #define TXPARSE_TAG
3
+
4
+ extern VALUE mTXParseTag;
5
+ void Init_TXParseTag();
6
+
7
+ #endif
data/lib/init_rails.rb ADDED
@@ -0,0 +1,16 @@
1
+ begin
2
+ require File.join(File.dirname(__FILE__), 'txprails') # From here
3
+ rescue LoadError
4
+ begin
5
+ require 'txprails' # From gem
6
+ rescue LoadError => e
7
+ # gems:install may be run to install TXPRails with the skeleton plugin
8
+ # but not the gem itself installed.
9
+ # Don't die if this is the case.
10
+ raise e unless defined?(Rake) && Rake.application.top_level_tasks.include?('gems:install')
11
+ end
12
+ end
13
+
14
+ # Load TXPRails.
15
+ # TXPRails may be undefined if we're running gems:install.
16
+ TXPRails.init_rails if defined?(TXPRails)
@@ -0,0 +1,88 @@
1
+ require 'optparse'
2
+ require 'fileutils'
3
+
4
+ module TXPRails
5
+ # This module handles miscelaneous command line utilities
6
+ class Commands
7
+ # @param args [Array<String>] The command-line arguments
8
+ def initialize(args)
9
+ @args = args
10
+ @options = {}
11
+ @options[:requires] = [] # ??
12
+ @options[:load_paths] = [] # ??
13
+ end
14
+
15
+ # Parses the command-line arguments and runs the executable.
16
+ # Calls `Kernel#exit` at the end, so it never returns.
17
+ def parse!
18
+ begin
19
+ @opts = OptionParser.new(&method(:set_opts))
20
+ @opts.parse!(@args)
21
+ @options
22
+ rescue Exception => e
23
+ raise e if e.is_a?(SystemExit)
24
+
25
+ $stderr.puts e.message
26
+ exit 1
27
+ end
28
+ exit 0
29
+ end
30
+
31
+ # @return [String] A description of the executable
32
+ def to_s
33
+ @opts.to_s
34
+ end
35
+
36
+ protected
37
+
38
+ # Tells optparse how to parse the arguments
39
+ # available for all executables.
40
+ #
41
+ # This is meant to be overridden by subclasses
42
+ # so they can add their own options.
43
+ #
44
+ # @param opts [OptionParser]
45
+ def set_opts(opts)
46
+ opts.on_tail("-?", "-h", "--help", "Show this message") do
47
+ puts opts
48
+ exit
49
+ end
50
+
51
+ opts.on_tail("-v", "--version", "Print version") do
52
+ puts("TXPRails #{::TXPRails.version[:string]}")
53
+ exit
54
+ end
55
+
56
+ opts.on('--rails RAILS_DIR', "Install TXPRails from the Gem to a Rails project") do |dir|
57
+ original_dir = dir
58
+ dir = File.join(dir, 'vendor', 'plugins')
59
+ unless File.exists?(dir)
60
+ puts "Directory #{dir} doesn't exist"
61
+ exit
62
+ end
63
+
64
+ dir = File.join(dir, 'txprails')
65
+ if File.exists?(dir)
66
+ print "Directory #{dir} already exists, overwrite [y/N]? "
67
+ exit if gets !~ /y/i
68
+ FileUtils.rm_rf(dir)
69
+ end
70
+
71
+ begin
72
+ Dir.mkdir(dir)
73
+ rescue SystemCallError
74
+ puts "Cannot create #{dir}"
75
+ exit
76
+ end
77
+
78
+ File.open(File.join(dir, 'init.rb'), 'w') do |file|
79
+ file << File.read(File.dirname(__FILE__) + "/../init_rails.rb")
80
+ end
81
+
82
+ puts "Haml plugin added to #{original_dir}"
83
+ exit
84
+ end
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,29 @@
1
+ # Simple template initialization code
2
+ # Borrowed ideas from Haml, but VERY much simplyfied
3
+ # WARNING: We choose to use the plugin approach, so NO RAILS < 2.1.0 compatibility!
4
+
5
+ module TXPRails
6
+
7
+ class TXPTemplateHandler < TXPRails::Util.av_template_class(:Handler)
8
+ include TXP
9
+
10
+ def initialize(view)
11
+ @view = view
12
+ end
13
+
14
+ def render(template, local_assigns = {})
15
+ parse(template.source)
16
+ end
17
+
18
+ def compilable?
19
+ false
20
+ end
21
+ end
22
+ end
23
+
24
+
25
+ if defined? ActionView::Template and ActionView::Template.respond_to? :register_template_handler
26
+ ActionView::Template
27
+ else
28
+ ActionView::Base
29
+ end.register_template_handler(:txp, TXPRails::TXPTemplateHandler)
@@ -0,0 +1,44 @@
1
+ module TXPRails
2
+ # A module containing various useful functions.
3
+ module Util
4
+ extend self
5
+
6
+ # An array of ints representing the Ruby version number.
7
+ RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}
8
+
9
+ # Returns the path of a file relative to the Haml root directory.
10
+ #
11
+ # @param file [String] The filename relative to the Haml root
12
+ # @return [String] The filename relative to the the working directory
13
+ def scope(file)
14
+ File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), file)
15
+ end
16
+
17
+ ## Cross Rails Version Compatibility
18
+
19
+ # Returns the root of the Rails application,
20
+ # if this is running in a Rails context.
21
+ # Returns `nil` if no such root is defined.
22
+ #
23
+ # @return [String, nil]
24
+ def rails_root
25
+ return Rails.root.to_s if defined?(Rails.root)
26
+ return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
27
+ return nil
28
+ end
29
+
30
+ # Returns an ActionView::Template* class.
31
+ # In pre-3.0 versions of Rails, most of these classes
32
+ # were of the form `ActionView::TemplateFoo`,
33
+ # while afterwards they were of the form `ActionView::Template::Foo`.
34
+ #
35
+ # @param name [#to_s] The name of the class to get.
36
+ # For example, `:Error` will return `ActionView::TemplateError`
37
+ # or `ActionView::Template::Error`.
38
+ def av_template_class(name)
39
+ return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
40
+ return ActionView::Template.const_get(name.to_s)
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,64 @@
1
+ require 'txprails/util'
2
+
3
+ module TXPRails
4
+ # Handles TXPRails version-reporting.
5
+ # TXPRails not only reports the standard three version numbers,
6
+ # but its Git revision hash as well,
7
+ # if it was installed from Git.
8
+ module Version
9
+ include TXPRails::Util
10
+
11
+ # Returns a hash representing the version of TXPRails.
12
+ # The `:major`, `:minor`, and `:teeny` keys have their respective numbers as Fixnums.
13
+ # The `:name` key has the name of the version.
14
+ # The `:string` key contains a human-readable string representation of the version.
15
+ # The `:number` key is the major, minor, and teeny keys separated by periods.
16
+ # If TXPRails is checked out from Git, the `:rev` key will have the revision hash.
17
+ # For example:
18
+ #
19
+ # {
20
+ # :string => "2.1.0.9616393",
21
+ # :rev => "9616393b8924ef36639c7e82aa88a51a24d16949",
22
+ # :number => "2.1.0",
23
+ # :major => 2, :minor => 1, :teeny => 0
24
+ # }
25
+ #
26
+ # @return [{Symbol => String/Fixnum}] The version hash
27
+ def version
28
+ return @@version if defined?(@@version)
29
+
30
+ numbers = File.read(scope('VERSION')).strip.split('.').map { |n| n.to_i }
31
+ name = File.read(scope('VERSION_NAME')).strip
32
+ @@version = {
33
+ :major => numbers[0],
34
+ :minor => numbers[1],
35
+ :teeny => numbers[2],
36
+ :name => name
37
+ }
38
+ @@version[:number] = [:major, :minor, :teeny].map { |comp| @@version[comp] }.compact.join('.')
39
+ @@version[:string] = @@version[:number].dup
40
+
41
+ if File.exists?(scope('REVISION'))
42
+ rev = File.read(scope('REVISION')).strip
43
+ rev = nil if rev !~ /^([a-f0-9]+|\(.*\))$/
44
+ end
45
+
46
+ if (rev.nil? || rev == '(unknown)') && File.exists?(scope('.git/HEAD'))
47
+ rev = File.read(scope('.git/HEAD')).strip
48
+ if rev =~ /^ref: (.*)$/
49
+ rev = File.read(scope(".git/#{$1}")).strip
50
+ end
51
+ end
52
+
53
+ if rev
54
+ @@version[:rev] = rev
55
+ unless rev[0] == ?(
56
+ @@version[:string] << "." << rev[0...7]
57
+ end
58
+ @@version[:string] << " (#{name})"
59
+ end
60
+
61
+ @@version
62
+ end
63
+ end
64
+ end
data/lib/txprails.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'txprails/txparse'
2
+ require 'txprails/version'
3
+
4
+ module TXPRails
5
+ extend TXPRails::Version
6
+
7
+ # A string representing the version of TXPRails.
8
+ # A more fine-grained representation is available from TXPRails.version.
9
+ VERSION = version[:string] unless defined?(TXPRails::VERSION)
10
+
11
+ # Initializes TXPRails for Rails.
12
+ #
13
+ # This method is called by `init.rb`,
14
+ # which is run by Rails on startup.
15
+ # We use it rather than putting stuff straight into `init.rb`
16
+ # so we can change the initialization behavior
17
+ # without modifying the file itself.
18
+ #
19
+ def self.init_rails
20
+ %w[txprails/template].each {|f| require f}
21
+ end
22
+ end
23
+
24
+ require 'txprails/util'
25
+
26
+ module TXP
27
+ include TXParse
28
+
29
+ def txp_doc(tag)
30
+ "<!DOCTYPE html>"
31
+ end
32
+
33
+ def txp_content(tag)
34
+ if content_for_layout = @view.instance_variable_get("@content_for_layout")
35
+ parse(content_for_layout)
36
+ end
37
+ end
38
+
39
+ def txp_include(tag)
40
+ @view.render :partial => tag.attr["name"]
41
+ end
42
+
43
+ # self.extend self
44
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'txprails'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestTxprails < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end