slippers 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. data/LICENSE +20 -0
  2. data/README +56 -0
  3. data/Rakefile +27 -0
  4. data/VERSION.yml +4 -0
  5. data/examples/blog/README +3 -0
  6. data/examples/blog/blog.db +0 -0
  7. data/examples/blog/controller/main.rb +30 -0
  8. data/examples/blog/model/entry.rb +31 -0
  9. data/examples/blog/public/styles/blog.css +132 -0
  10. data/examples/blog/spec/blog.rb +87 -0
  11. data/examples/blog/start.rb +7 -0
  12. data/examples/blog/view/edit.st +19 -0
  13. data/examples/blog/view/edit.xhtml +17 -0
  14. data/examples/blog/view/entry.st +12 -0
  15. data/examples/blog/view/index.st +4 -0
  16. data/examples/blog/view/index.xhtml +17 -0
  17. data/examples/blog/view/layout.st +11 -0
  18. data/examples/blog/view/layout.xhtml +11 -0
  19. data/examples/blog/view/new.st +16 -0
  20. data/examples/blog/view/new.xhtml +16 -0
  21. data/examples/forms/Rakefile +22 -0
  22. data/examples/forms/controller/init.rb +12 -0
  23. data/examples/forms/controller/main.rb +20 -0
  24. data/examples/forms/controller/registration.rb +24 -0
  25. data/examples/forms/forms.db +0 -0
  26. data/examples/forms/model/core_extensions/string_extensions.rb +5 -0
  27. data/examples/forms/model/field.rb +33 -0
  28. data/examples/forms/model/form.rb +33 -0
  29. data/examples/forms/model/init.rb +18 -0
  30. data/examples/forms/model/orm/registration.rb +12 -0
  31. data/examples/forms/model/registration_form_builder.rb +7 -0
  32. data/examples/forms/model/registration_repository.rb +39 -0
  33. data/examples/forms/model/registration_rules/incorrect_email_format.rb +11 -0
  34. data/examples/forms/model/registration_rules/number_field.rb +10 -0
  35. data/examples/forms/model/registration_rules/required_field.rb +9 -0
  36. data/examples/forms/model/registration_validator.rb +17 -0
  37. data/examples/forms/public/css/ramaze_error.css +90 -0
  38. data/examples/forms/public/dispatch.fcgi +11 -0
  39. data/examples/forms/public/favicon.ico +0 -0
  40. data/examples/forms/public/js/jquery.js +3549 -0
  41. data/examples/forms/public/ramaze.png +0 -0
  42. data/examples/forms/public/web-application.js +0 -0
  43. data/examples/forms/spec/form_spec.rb +16 -0
  44. data/examples/forms/spec/main.rb +25 -0
  45. data/examples/forms/spec/registration_controller_spec.rb +0 -0
  46. data/examples/forms/spec/registration_repository_spec.rb +38 -0
  47. data/examples/forms/spec/registration_validator_spec.rb +53 -0
  48. data/examples/forms/spec/spec_helper.rb +3 -0
  49. data/examples/forms/start.rb +12 -0
  50. data/examples/forms/start.ru +16 -0
  51. data/examples/forms/view/error.xhtml +64 -0
  52. data/examples/forms/view/index.xhtml +34 -0
  53. data/examples/forms/view/page.xhtml +27 -0
  54. data/examples/forms/view/registration/index.st +26 -0
  55. data/examples/main_controller.rb +40 -0
  56. data/examples/start.rb +7 -0
  57. data/examples/todolist/README +1 -0
  58. data/examples/todolist/controller/main.rb +71 -0
  59. data/examples/todolist/layout/page.rb +31 -0
  60. data/examples/todolist/model/tasks.rb +14 -0
  61. data/examples/todolist/public/favicon.ico +0 -0
  62. data/examples/todolist/public/js/jquery.js +1923 -0
  63. data/examples/todolist/public/ramaze.png +0 -0
  64. data/examples/todolist/start.rb +11 -0
  65. data/examples/todolist/todolist.db +11 -0
  66. data/examples/todolist/view/index.st +39 -0
  67. data/examples/todolist/view/index.xhtml +17 -0
  68. data/examples/todolist/view/new.st +41 -0
  69. data/examples/todolist/view/new.xhtml +7 -0
  70. data/examples/todolist/view/tasks.st +6 -0
  71. data/examples/todolist.db +5 -0
  72. data/examples/view/index.st +11 -0
  73. data/examples/view/person/age.st +1 -0
  74. data/examples/view/person/age_renderer.rb +6 -0
  75. data/examples/view/person/index.st +2 -0
  76. data/lib/engine/binding_wrapper.rb +10 -0
  77. data/lib/engine/engine.rb +28 -0
  78. data/lib/engine/file_template.rb +12 -0
  79. data/lib/engine/slippers.treetop +169 -0
  80. data/lib/engine/slippers_nodes.rb +119 -0
  81. data/lib/engine/template.rb +17 -0
  82. data/lib/engine/template_group.rb +40 -0
  83. data/lib/engine/template_group_directory.rb +57 -0
  84. data/lib/ramazeTemplates/0001-Adding-slippers-as-a-new-template.patch +138 -0
  85. data/lib/ramazeTemplates/0002-Fixing-problem-with-using-other-renderers.patch +27 -0
  86. data/lib/ramazeTemplates/slippers.rb +22 -0
  87. data/lib/slippers.rb +14 -0
  88. data/spec/binding_wrapper.rb +19 -0
  89. data/spec/engine.rb +89 -0
  90. data/spec/file_template.rb +9 -0
  91. data/spec/parse_attributes.rb +98 -0
  92. data/spec/parse_renderers.rb +35 -0
  93. data/spec/parse_templates.rb +73 -0
  94. data/spec/person_template.st +1 -0
  95. data/spec/spec_helper.rb +14 -0
  96. data/spec/template_group.rb +52 -0
  97. data/spec/template_group_directory.rb +43 -0
  98. data/spec/views/index.st +1 -0
  99. data/spec/views/money.rb +2 -0
  100. data/spec/views/person/age.st +1 -0
  101. data/spec/views/person/date_renderer.rb +5 -0
  102. data/spec/views/person/name.st +1 -0
  103. metadata +218 -0
Binary file
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2006 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+
4
+ require 'rubygems'
5
+ require 'ramaze'
6
+
7
+ require 'controller/main'
8
+ #require 'layout/page'
9
+ require 'model/tasks'
10
+
11
+ Ramaze.start :port => 3000
@@ -0,0 +1,11 @@
1
+ ---
2
+ Watch the tv:
3
+ :done: false
4
+ Wash dishes:
5
+ :done: true
6
+ Clean the shower:
7
+ :done: false
8
+ Laundry:
9
+ :done: false
10
+ Vacuum the room:
11
+ :done: false
@@ -0,0 +1,39 @@
1
+ <html>
2
+ <head>
3
+ <title>TodoList</title>
4
+ <style>
5
+ table {
6
+ width: 100%;
7
+ }
8
+
9
+ tr {
10
+ background: #efe;
11
+ width: 100%;
12
+ }
13
+
14
+ tr:hover {
15
+ background: #dfd;
16
+ }
17
+
18
+ td.title {
19
+ font-weight: bold;
20
+ width: 60%;
21
+ }
22
+
23
+ td.status {
24
+ margin: 1em;
25
+ }
26
+
27
+ a {
28
+ color: #3a3;
29
+ }
30
+ </style>
31
+ </head>
32
+ <body>
33
+ <h1>$heading$</h1>
34
+ <a href="/new">New Task</a>
35
+ <table>
36
+ $tasks:tasks()$
37
+ </table>
38
+ </body>
39
+ </html>
@@ -0,0 +1,17 @@
1
+ <Page title="TodoList">
2
+ <a href="/new">New Task</a>
3
+ <?r if @tasks.empty? ?>
4
+ No Tasks
5
+ <?r else ?>
6
+ <table>
7
+ <?r @tasks.each do |title, status, toggle, delete| ?>
8
+ <tr>
9
+ <td class="title"> #{h title} </td>
10
+ <td class="status"> #{status} </td>
11
+ <td class="toggle"> #{toggle} </td>
12
+ <td class="delete"> #{delete} </td>
13
+ </tr>
14
+ <?r end ?>
15
+ </table>
16
+ <?r end ?>
17
+ </Page>
@@ -0,0 +1,41 @@
1
+ <html>
2
+ <head>
3
+ <title>TodoList</title>
4
+ <style>
5
+ table {
6
+ width: 100%;
7
+ }
8
+
9
+ tr {
10
+ background: #efe;
11
+ width: 100%;
12
+ }
13
+
14
+ tr:hover {
15
+ background: #dfd;
16
+ }
17
+
18
+ td.title {
19
+ font-weight: bold;
20
+ width: 60%;
21
+ }
22
+
23
+ td.status {
24
+ margin: 1em;
25
+ }
26
+
27
+ a {
28
+ color: #3a3;
29
+ }
30
+ </style>
31
+ </head>
32
+ <body>
33
+ <h1>New Task</h1>
34
+ <a href="/">Back to TodoList</a>
35
+ <form method="POST" action="create">
36
+ Task: <input type="text" name="title" /><br />
37
+ <input type="submit" />
38
+ </form>
39
+
40
+ </body>
41
+ </html>
@@ -0,0 +1,7 @@
1
+ <Page title="New Task">
2
+ <a href="/">Back to TodoList</a>
3
+ <form method="POST" action="create">
4
+ Task: <input type="text" name="title" /><br />
5
+ <input type="submit" />
6
+ </form>
7
+ </Page>
@@ -0,0 +1,6 @@
1
+ <tr>
2
+ <td class="title">$title$</td>
3
+ <td class="status">$status$</td>
4
+ <td class="status"><a href="/$toggle$/$resource$">$toggle$ tasks</a></td>
5
+ <td class="status"><a href="/delete/$resource$">delete</a></td>
6
+ </tr>
@@ -0,0 +1,5 @@
1
+ ---
2
+ Wash dishes:
3
+ :done: false
4
+ Laundry:
5
+ :done: false
@@ -0,0 +1,11 @@
1
+ <html>
2
+ <head>
3
+ <title>Slippers</title>
4
+ </head>
5
+ <body>
6
+ $person:person/index()$
7
+ <div>
8
+ This is authored by $author:author()$.
9
+ </div>
10
+ </body>
11
+ </html>
@@ -0,0 +1 @@
1
+ is $person/age_renderer()$ years old
@@ -0,0 +1,6 @@
1
+ class AgeRenderer
2
+ def render(date)
3
+ age = DateTime.now.year - date.year
4
+ age.to_s
5
+ end
6
+ end
@@ -0,0 +1,2 @@
1
+ <h3>Introducing....</h3>
2
+ <p>the faboulous <strong>$name$</strong> who $dob:person/age()$ is a $role$</p>
@@ -0,0 +1,10 @@
1
+ module Slippers
2
+ class BindingWrapper
3
+ def initialize(bindings)
4
+ @bindings = bindings
5
+ end
6
+ def [](method)
7
+ eval('@'+ method.to_s, @bindings)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ Treetop.load File.dirname(__FILE__) + '/slippers'
2
+
3
+ module Slippers
4
+ class Engine
5
+ DEFAULT_STRING = ''
6
+ def initialize(template, params={})
7
+ @main_template = Slippers::Template.new(template)
8
+ @template_group = params[:template_group]
9
+ end
10
+ attr_reader :main_template, :template_group
11
+
12
+ def render(object_to_render=nil)
13
+ parser = SlippersParser.new
14
+ parse_tree = parser.parse(@main_template.template)
15
+ return '' unless parse_tree
16
+ parse_tree.eval(object_to_render, @template_group)
17
+ end
18
+
19
+ def eql?(other)
20
+ @main_template.eql?(other.main_template) && @template_group.eql?(other.template_group)
21
+ end
22
+
23
+ def hash
24
+ @main_template.hash + @template_group.hash*23
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ module Slippers
2
+ class FileTemplate < Template
3
+ def initialize(filename)
4
+ @filename = filename
5
+ end
6
+ attr_reader :filename
7
+ def template()
8
+ return @template if @template
9
+ @template = File.read(@filename)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,169 @@
1
+ grammar Slippers
2
+
3
+ rule expression
4
+ before:some_text expression_to_render space after:expression <ExpressionNode> / some_text
5
+ end
6
+
7
+ rule expression_to_render
8
+ conditional_template / templated_expression
9
+ end
10
+
11
+ rule templated_expression
12
+ delimiter foo delimiter <TemplatedExpressionNode>
13
+ end
14
+
15
+ rule foo
16
+ apply_attribute_to_template / template / expression_option / attribute
17
+ end
18
+
19
+ rule apply_attribute_to_template
20
+ attribute apply_op template <ApplyAttributeToTemplateNode>
21
+ end
22
+
23
+ rule template
24
+ known_template / anonymous_template
25
+ end
26
+
27
+ rule known_template
28
+ template_path brackets <TemplateNode>
29
+ end
30
+
31
+ rule anonymous_template
32
+ "{" anonymous_template_words "}" <AnonymousTemplateNode>
33
+ end
34
+
35
+ rule attribute
36
+ anything_except_keywords <AttributeToRenderNode>
37
+ end
38
+
39
+ rule expression_option
40
+ attribute:anything_except_keywords ';' space null_subtitute:('null="' nulls:anything_except_keywords '"')? (",")? space seperator_value:('seperator="' seps:anything_except_keywords '"' )? <AttributeWithExpressionOptionNode>
41
+ end
42
+
43
+ rule conditional_template
44
+ "$if(" if_clause:attribute ")$" if_expression:expression else_clause:('$else$' else_expression:expression)? '$end$' <ConditionalTemplateNode>
45
+
46
+ end
47
+
48
+ rule some_text
49
+ not_delimiter* {
50
+ def eval(*args)
51
+ to_s
52
+ end
53
+ def to_s
54
+ text_value
55
+ end
56
+ }
57
+ end
58
+
59
+ rule anonymous_template_words
60
+ not_curly* {
61
+ def to_s
62
+ text_value
63
+ end
64
+ }
65
+ end
66
+
67
+ rule template_path
68
+ anything_except_keywords {
69
+ def to_s
70
+ text_value
71
+ end
72
+ }
73
+ end
74
+
75
+ rule anything_except_keywords
76
+ not_keyword* {
77
+ def eval(*args)
78
+ to_s
79
+ end
80
+ def to_s
81
+ text_value
82
+ end
83
+ }
84
+ end
85
+
86
+ rule word_with_underscore
87
+ word ("_" word)*
88
+ end
89
+
90
+ rule word
91
+ [a-zA-Z]+ {
92
+ def to_s
93
+ text_value
94
+ end
95
+ }
96
+ end
97
+
98
+ rule space
99
+ ' '* {
100
+ def eval(object_to_render=nil, template_group=nil)
101
+ to_s
102
+ end
103
+ def to_s
104
+ text_value
105
+ end
106
+ }
107
+ end
108
+
109
+ rule apply_op
110
+ ':' {
111
+ def to_s
112
+ text_value
113
+ end
114
+ }
115
+ end
116
+
117
+ rule delimiter
118
+ '$' {
119
+ def to_s
120
+ text_value
121
+ end
122
+ }
123
+ end
124
+
125
+ rule brackets
126
+ '()' {
127
+ def to_s
128
+ text_value
129
+ end
130
+ }
131
+ end
132
+
133
+ rule escape
134
+ '\\'
135
+ end
136
+
137
+ rule not_delimiter
138
+ escape delimiter / !delimiter . {
139
+ def to_s
140
+ text_value
141
+ end
142
+ }
143
+ end
144
+
145
+ rule not_apply_op
146
+ !apply_op . {
147
+ def to_s
148
+ text_value
149
+ end
150
+ }
151
+ end
152
+
153
+ rule not_keyword
154
+ !(apply_op / delimiter / brackets / ';' / '"' / ")" / "if" / "else" / "end" ) . {
155
+ def to_s
156
+ text_value
157
+ end
158
+ }
159
+ end
160
+
161
+ rule not_curly
162
+ !("{" / "}") . {
163
+ def to_s
164
+ text_value
165
+ end
166
+ }
167
+ end
168
+
169
+ end
@@ -0,0 +1,119 @@
1
+ module Slippers
2
+ module AttributeToRenderNode
3
+
4
+ def eval(object_to_render, template_group)
5
+ [object_to_render].flatten.inject('') { |rendered, item| rendered + render(value_of(item), template_group) }
6
+ end
7
+
8
+ def value_of(item)
9
+ return '' if attribute == ''
10
+ return item.to_s if attribute == 'it'
11
+ return item[to_sym] if item.respond_to?('[]'.to_sym) && item[to_sym]
12
+ return item.send(attribute) if item.respond_to?(attribute)
13
+ Slippers::Engine::DEFAULT_STRING
14
+ end
15
+
16
+ def render(object_to_render, template_group)
17
+ substitue_null_values(object_to_render)
18
+ return template_group.render(object_to_render) if template_group && template_group.has_registered?(object_to_render.class)
19
+ return object_to_render.compact.join(seperator) if seperator && object_to_render.respond_to?('join')
20
+ object_to_render.to_s
21
+ end
22
+
23
+ def substitue_null_values(object_to_render)
24
+ return unless null_values && object_to_render.respond_to?('map!')
25
+ object_to_render.map!{|x| if x then x else null_values end }
26
+ end
27
+
28
+ def to_sym
29
+ attribute.to_sym
30
+ end
31
+
32
+ def null_values
33
+ end
34
+
35
+ def seperator
36
+ end
37
+
38
+ def attribute
39
+ text_value
40
+ end
41
+ end
42
+
43
+ class AttributeWithExpressionOptionNode < Treetop::Runtime::SyntaxNode
44
+ include AttributeToRenderNode
45
+ def seperator
46
+ seperator_value.seps.to_s if seperator_value.elements
47
+ end
48
+ def null_values
49
+ null_subtitute.nulls.to_s if null_subtitute.elements
50
+ end
51
+ end
52
+
53
+ class TemplateNode < Treetop::Runtime::SyntaxNode
54
+
55
+ def eval(object_to_render, template_group)
56
+ apply_attribute_to_subtemplate(object_to_render, template_group)
57
+ end
58
+
59
+ def apply_attribute_to_subtemplate(item, template_group)
60
+ return '' unless template_group
61
+ subtemplate = template_group.find(template_path.to_s)
62
+ return '' unless (subtemplate && subtemplate.respond_to?('render'))
63
+ subtemplate.render(item)
64
+ end
65
+ end
66
+
67
+ class AnonymousTemplateNode < Treetop::Runtime::SyntaxNode
68
+
69
+ def eval(object_to_render, template_group)
70
+ apply_attribute_to_subtemplate(object_to_render, template_group)
71
+ end
72
+
73
+ def apply_attribute_to_subtemplate(item, template_group)
74
+ SlippersParser.new.parse(anonymous_template_words.to_s).eval(item, template_group)
75
+ end
76
+ end
77
+
78
+ class ApplyAttributeToTemplateNode < Treetop::Runtime::SyntaxNode
79
+
80
+ def eval(object_to_render, template_group)
81
+ [object_to_render].flatten.inject('') { |rendered, item| rendered + find_attribute_and_render(item, template_group) }
82
+ end
83
+
84
+ def find_attribute_and_render(item, template_group)
85
+ object_to_render = attribute.value_of(item)
86
+ [object_to_render].flatten.inject('') { |rendered, i| rendered + template.apply_attribute_to_subtemplate(i, template_group).to_s }
87
+ end
88
+ end
89
+
90
+ class ConditionalTemplateNode < Treetop::Runtime::SyntaxNode
91
+
92
+ def eval(object_to_render, template_group)
93
+ attribute = if_clause.value_of(object_to_render)
94
+ if (attribute && attribute != Slippers::Engine::DEFAULT_STRING) then
95
+ if_expression.eval(object_to_render, template_group)
96
+ else
97
+ if else_clause.elements then else_clause.else_expression.eval(object_to_render, template_group) else Slippers::Engine::DEFAULT_STRING end
98
+ end
99
+ end
100
+
101
+
102
+ end
103
+
104
+ class TemplatedExpressionNode < Treetop::Runtime::SyntaxNode
105
+
106
+ def eval(object_to_render, template_group)
107
+ foo.eval(object_to_render, template_group)
108
+ end
109
+
110
+ end
111
+
112
+ class ExpressionNode < Treetop::Runtime::SyntaxNode
113
+
114
+ def eval(object_to_render, template_group=nil)
115
+ before.eval(object_to_render, template_group) + expression_to_render.eval(object_to_render, template_group) + space.eval + after.eval(object_to_render, template_group)
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,17 @@
1
+ module Slippers
2
+ class Template
3
+ def initialize(template)
4
+ @template = template
5
+ end
6
+ attr_reader :template
7
+
8
+ def eql?(other)
9
+ template.eql?(other.template)
10
+ end
11
+
12
+ def hash
13
+ template.hash
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ module Slippers
2
+ class TemplateGroup
3
+ def initialize(params={})
4
+ @templates = params[:templates]
5
+ @super_group = params[:super_group]
6
+ end
7
+
8
+ def find(subtemplate)
9
+ return nil unless @templates
10
+ return create_template(subtemplate.to_sym) if @templates.include?(subtemplate.to_sym)
11
+ find_in_super_group(subtemplate)
12
+ end
13
+
14
+ def has_registered?(class_name)
15
+ return false unless @templates
16
+ return true if @templates.include?(class_name)
17
+ return false unless @super_group
18
+ @super_group.has_registered?(class_name)
19
+ end
20
+
21
+ def render(item)
22
+ return '' unless @templates
23
+ return @templates[item.class].render(item) if has_registered?(item.class)
24
+ return '' unless @super_group
25
+ @super_group.render(item)
26
+ end
27
+
28
+ private
29
+ def find_in_super_group(subtemplate)
30
+ return nil unless @super_group
31
+ @super_group.find(subtemplate)
32
+ end
33
+
34
+ def create_template(subtemplate)
35
+ template = @templates[subtemplate]
36
+ return template unless template.is_a?(String)
37
+ Slippers::Engine.new(template)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,57 @@
1
+ module Slippers
2
+ class TemplateGroupDirectory < TemplateGroup
3
+ def initialize(directory_paths, params={})
4
+ @directory_paths = directory_paths
5
+ @super_group = params[:super_group]
6
+ end
7
+ attr_reader :directory_paths
8
+
9
+ def find(subtemplate)
10
+ file_name = @directory_paths.map { |directory_path| directory_path + '/' + subtemplate + '.st' }.find { |f| File.exist? f}
11
+ return Engine.new(FileTemplate.new(file_name).template, :template_group => self) if file_name
12
+ find_renderer_or_supergroup(subtemplate)
13
+ end
14
+
15
+ def has_registered?(class_name)
16
+ return false unless @super_group
17
+ @super_group.has_registered?(class_name)
18
+ end
19
+
20
+ def render(item)
21
+ return '' unless @super_group
22
+ @super_group.render(item)
23
+ end
24
+
25
+
26
+ def eql?(other)
27
+ return false unless other
28
+ directory_paths.eql?(other.directory_paths)
29
+ end
30
+ def hash
31
+ @directory_paths.hash
32
+ end
33
+
34
+ private
35
+ def find_renderer_or_supergroup(subtemplate)
36
+ file_name = @directory_paths.map { |directory_path| directory_path + '/' + subtemplate + '.rb' }.find { |f| File.exist? f}
37
+ return find_in_super_group(subtemplate) unless file_name
38
+
39
+ renderer_name = subtemplate.split('/')[-1]
40
+ load File.expand_path(file_name)
41
+ create_renderer renderer_name
42
+ end
43
+
44
+ def find_in_super_group(subtemplate)
45
+ return nil unless @super_group
46
+ @super_group.find(subtemplate)
47
+ end
48
+
49
+ def create_renderer(renderer_name)
50
+ camelCase = renderer_name.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
51
+ classes = camelCase.split('::')
52
+ classes.shift if classes.empty? or classes.first.empty?
53
+ const = classes.inject(Object){ |constant, class_name| constant.const_get(class_name) }
54
+ const.new
55
+ end
56
+ end
57
+ end