honkster-erector 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/README.txt +116 -0
  2. data/VERSION.yml +4 -0
  3. data/bin/erector +14 -0
  4. data/lib/erector.rb +34 -0
  5. data/lib/erector/abstract_widget.rb +172 -0
  6. data/lib/erector/after_initialize.rb +34 -0
  7. data/lib/erector/caching.rb +93 -0
  8. data/lib/erector/convenience.rb +58 -0
  9. data/lib/erector/dependencies.rb +24 -0
  10. data/lib/erector/dependency.rb +30 -0
  11. data/lib/erector/erect/erect.rb +160 -0
  12. data/lib/erector/erect/erected.rb +75 -0
  13. data/lib/erector/erect/indenting.rb +36 -0
  14. data/lib/erector/erect/rhtml.treetop +233 -0
  15. data/lib/erector/errors.rb +12 -0
  16. data/lib/erector/extensions/hash.rb +21 -0
  17. data/lib/erector/extensions/object.rb +18 -0
  18. data/lib/erector/externals.rb +97 -0
  19. data/lib/erector/html.rb +352 -0
  20. data/lib/erector/inline.rb +37 -0
  21. data/lib/erector/jquery.rb +36 -0
  22. data/lib/erector/mixin.rb +12 -0
  23. data/lib/erector/needs.rb +94 -0
  24. data/lib/erector/output.rb +117 -0
  25. data/lib/erector/rails.rb +27 -0
  26. data/lib/erector/rails/extensions/action_controller.rb +16 -0
  27. data/lib/erector/rails/extensions/rails_helpers.rb +159 -0
  28. data/lib/erector/rails/extensions/rails_widget.rb +126 -0
  29. data/lib/erector/rails/rails_form_builder.rb +24 -0
  30. data/lib/erector/rails/rails_version.rb +6 -0
  31. data/lib/erector/rails/template_handlers/ert_handler.rb +32 -0
  32. data/lib/erector/rails/template_handlers/rb_handler.rb +52 -0
  33. data/lib/erector/raw_string.rb +8 -0
  34. data/lib/erector/sass.rb +22 -0
  35. data/lib/erector/unicode.rb +18185 -0
  36. data/lib/erector/unicode_builder.rb +67 -0
  37. data/lib/erector/version.rb +12 -0
  38. data/lib/erector/widget.rb +54 -0
  39. data/lib/erector/widgets.rb +6 -0
  40. data/lib/erector/widgets/environment_badge.rb +29 -0
  41. data/lib/erector/widgets/external_renderer.rb +51 -0
  42. data/lib/erector/widgets/field_table.rb +110 -0
  43. data/lib/erector/widgets/form.rb +30 -0
  44. data/lib/erector/widgets/page.rb +165 -0
  45. data/lib/erector/widgets/table.rb +104 -0
  46. data/rails/init.rb +4 -0
  47. data/spec/erect/erect_rails_spec.rb +114 -0
  48. data/spec/erect/erect_spec.rb +175 -0
  49. data/spec/erect/erected_spec.rb +164 -0
  50. data/spec/erect/rhtml_parser_spec.rb +361 -0
  51. data/spec/erector/caching_spec.rb +269 -0
  52. data/spec/erector/convenience_spec.rb +259 -0
  53. data/spec/erector/dependency_spec.rb +67 -0
  54. data/spec/erector/externals_spec.rb +236 -0
  55. data/spec/erector/html_spec.rb +509 -0
  56. data/spec/erector/indentation_spec.rb +211 -0
  57. data/spec/erector/inline_spec.rb +94 -0
  58. data/spec/erector/jquery_spec.rb +35 -0
  59. data/spec/erector/mixin_spec.rb +65 -0
  60. data/spec/erector/needs_spec.rb +120 -0
  61. data/spec/erector/output_spec.rb +199 -0
  62. data/spec/erector/sample-file.txt +1 -0
  63. data/spec/erector/sass_spec.rb +33 -0
  64. data/spec/erector/unicode_builder_spec.rb +75 -0
  65. data/spec/erector/widget_spec.rb +250 -0
  66. data/spec/erector/widgets/field_table_spec.rb +133 -0
  67. data/spec/erector/widgets/form_spec.rb +31 -0
  68. data/spec/erector/widgets/page_spec.rb +85 -0
  69. data/spec/erector/widgets/table_spec.rb +99 -0
  70. data/spec/spec_helper.rb +95 -0
  71. metadata +191 -0
@@ -0,0 +1,67 @@
1
+ # Note that this class is only used in building erector itself
2
+ # (and even then, only needs to be run when there is a new
3
+ # UnicodeData.txt file from unicode.org).
4
+ class Erector::UnicodeBuilder
5
+
6
+ def initialize(input, output)
7
+ @input = input
8
+ @output = output
9
+ @first = true
10
+ end
11
+
12
+ def generate()
13
+ @output.puts "Erector::CHARACTERS = {"
14
+ process_file
15
+ @output.puts "}"
16
+ end
17
+
18
+ def process_file()
19
+ while !@input.eof
20
+ line = @input.gets.strip
21
+ if (line == "")
22
+ next;
23
+ end
24
+
25
+ process_line(line)
26
+ end
27
+ if (!@first)
28
+ @output.puts
29
+ end
30
+ end
31
+
32
+ def output_line(line)
33
+ if (!@first)
34
+ @output.puts(',')
35
+ end
36
+
37
+ @output.print(line)
38
+
39
+ @first = false
40
+ end
41
+
42
+ def process_line(line)
43
+ fields = line.split(';')
44
+ code_point = fields[0]
45
+ name = fields[1]
46
+ alternate_name = fields[10]
47
+
48
+ if /^</.match(name)
49
+ return ""
50
+ end
51
+
52
+ output name, code_point
53
+ if (!alternate_name.nil? && alternate_name != "")
54
+ output alternate_name, code_point
55
+ end
56
+ end
57
+
58
+ def output(name, code_point)
59
+ output_line " :#{namify(name)} => 0x#{code_point.downcase}"
60
+ end
61
+
62
+ def namify(name)
63
+ name.downcase.gsub(/[- ]/, '_')
64
+ end
65
+
66
+ end
67
+
@@ -0,0 +1,12 @@
1
+ require 'yaml'
2
+
3
+ ##
4
+ # Erector view framework
5
+ module Erector
6
+ if !Erector.const_defined?(:VERSION)
7
+ dir = File.dirname(__FILE__)
8
+ version = YAML.load_file(File.expand_path("#{dir}/../../VERSION.yml"))
9
+ VERSION = "#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
10
+ end
11
+ end
12
+
@@ -0,0 +1,54 @@
1
+ module Erector
2
+
3
+ # A Widget is the center of the Erector universe.
4
+ #
5
+ # To create a widget, extend Erector::Widget and implement the +content+
6
+ # method. Inside this method you may call any of the tag methods like +span+
7
+ # or +p+ to emit HTML/XML tags.
8
+ #
9
+ # You can also define a widget on the fly by passing a block to +new+. This
10
+ # block will get executed when the widget's +content+ method is called. See
11
+ # the userguide for important details about the scope of this block when run --
12
+ # http://erector.rubyforge.org/userguide.html#blocks
13
+ #
14
+ # To render a widget from the outside, instantiate it and call its +to_html+
15
+ # method.
16
+ #
17
+ # A widget's +new+ method optionally accepts an options hash. Entries in
18
+ # this hash are converted to instance variables.
19
+ #
20
+ # You can add runtime input checking via the +needs+ macro. See #needs.
21
+ # This mechanism is meant to ameliorate development-time confusion about
22
+ # exactly what parameters are supported by a given widget, avoiding
23
+ # confusing runtime NilClass errors.
24
+ #
25
+ # To call one widget from another, inside the parent widget's +content+
26
+ # method, instantiate the child widget and call the +widget+ method. This
27
+ # assures that the same output stream is used, which gives better
28
+ # performance than using +capture+ or +to_html+. It also preserves the
29
+ # indentation and helpers of the enclosing class.
30
+ #
31
+ # In this documentation we've tried to keep the distinction clear between
32
+ # methods that *emit* text and those that *return* text. "Emit" means that
33
+ # it writes to the output stream; "return" means that it returns a string
34
+ # like a normal method and leaves it up to the caller to emit that string if
35
+ # it wants.
36
+ #
37
+ # This class extends AbstractWidget and includes several modules,
38
+ # so be sure to check all of those places for API documentation for the
39
+ # various methods of Widget. Also read the API Cheatsheet in the user guide
40
+ # at http://erector.rubyforge.org/userguide#apicheatsheet
41
+ #
42
+ # Now, seriously, after playing around a bit, go read the user guide. It's
43
+ # fun!
44
+ class Widget < AbstractWidget
45
+ include Erector::HTML
46
+ include Erector::Needs
47
+ # include Erector::Caching
48
+ include Erector::Externals
49
+ include Erector::Convenience
50
+ include Erector::JQuery
51
+ include Erector::AfterInitialize
52
+ include Erector::Sass if Object.const_defined?(:Sass)
53
+ end
54
+ end
@@ -0,0 +1,6 @@
1
+ require "erector/widgets/environment_badge"
2
+ require "erector/widgets/field_table"
3
+ require "erector/widgets/form"
4
+ require "erector/widgets/external_renderer"
5
+ require "erector/widgets/page"
6
+ require "erector/widgets/table"
@@ -0,0 +1,29 @@
1
+ module Erector
2
+ module Widgets #:nodoc:
3
+
4
+ # Displays a colored badge in the upper-left corner
5
+ # signifying the environment the app is running in.
6
+ # Inspired by Assaf Arkin
7
+ # <http://blog.labnotes.org/2009/10/08/using-a-badge-to-distinguish-development-and-production-environments/>
8
+ # Erectorized by Alex Chaffee
9
+ class EnvironmentBadge < Erector::Widget
10
+ def content
11
+ style <<-STYLE
12
+ #environment_badge { position: fixed; left: 1em; font-weight: bold; padding: .2em 0.9em; text-transform: uppercase; display: none }
13
+ #environment_badge.staging { color: #000; background: #ffff00; border: 2px solid #cccc20; }
14
+ #environment_badge.development { color: #fff; background: #ff0000; border: 2px solid #cc2020; }
15
+ #environment_badge.staging, #environment_badge.development { border-top: none; display: block; opacity: 0.6 }
16
+ STYLE
17
+ unless environment =~ /production/
18
+ p environment, :class => environment, :id => "environment_badge"
19
+ end
20
+ end
21
+
22
+ def environment
23
+ RAILS_ENV
24
+ rescue NameError
25
+ ENV['RAILS_ENV'] || ENV['RACK_ENV']
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,51 @@
1
+ class ExternalRenderer < Erector::Widget
2
+ needs :classes
3
+ needs :included_stylesheets => true, :inline_styles => true, :included_scripts => true, :inline_scripts => true
4
+
5
+ def content
6
+ included_stylesheets if @included_stylesheets
7
+ inline_styles if @inline_styles
8
+ included_scripts if @included_scripts
9
+ inline_scripts if @inline_scripts
10
+ end
11
+
12
+ def rendered_externals(type)
13
+ @classes.map do |klass|
14
+ klass.dependencies(type)
15
+ end.flatten.uniq
16
+ end
17
+
18
+ def included_scripts
19
+ rendered_externals(:js).each do |external|
20
+ script({:type => "text/javascript", :src => external.text}.merge(external.options))
21
+ end
22
+ end
23
+
24
+ def included_stylesheets
25
+ rendered_externals(:css).each do |external|
26
+ link({:rel => "stylesheet", :href => external.text, :type => "text/css", :media => "all"}.merge(external.options))
27
+ end
28
+ end
29
+
30
+ def inline_styles
31
+ rendered_externals(:style).each do |external|
32
+ style({:type => "text/css", 'xml:space' => 'preserve'}.merge(external.options)) do
33
+ rawtext external.text
34
+ end
35
+ end
36
+ end
37
+
38
+ def inline_scripts
39
+ rendered_externals(:script).each do |external|
40
+ javascript external.options do
41
+ rawtext external.text
42
+ end
43
+ end
44
+ # todo: allow :load or :ready per external script
45
+ rendered_externals(:jquery).each do |external|
46
+ jquery :load, external.text, external.options
47
+ end
48
+ end
49
+
50
+ end
51
+
@@ -0,0 +1,110 @@
1
+ # A simple HTML table with three columns: label, contents, and (optionally) note.
2
+ # Each row is called a field.
3
+ # The last row can optionally contain "buttons" (actually any widget will do)
4
+ # that are all shown in the 2nd column.
5
+ # It's all surrounded with a fieldset element for a title.
6
+ #
7
+ # In ASCII art, it looks something like this:
8
+ #
9
+ # /-Meals----------------------------------------\
10
+ # | Breakfast | eggs | |
11
+ # | Lunch | sandwich | |
12
+ # | Dinner | lo mein | with shrimp! |
13
+ # | | [Save] [Cancel] | |
14
+ # \----------------------------------------------/
15
+ #
16
+ # There are two ways to create a FieldTable.
17
+ # 1. Pass a block in to the constructor.
18
+ # 2. Make a subclass.
19
+ # In both cases you'll want to call the "field" and "button" methods on the table.
20
+ # This sets up the contents which will be rendered later during FieldTable#content.
21
+ # If you make a subclass (#2) you can do this either in the constructor or in
22
+ # the content method *before* you call super.
23
+ #
24
+ # The FieldTable is surrounded by a fieldset element whose legend is the "title" instance variable.
25
+ # Inside this fieldset is a table element.
26
+ # Each field (row of the table) has a label (th), a content cell (td), and an optional note (td).
27
+ #
28
+ # If you call "button" you can pass in a block that'll get rendered inside the 2nd column of the last row. The idea here is that you might want to make an HTML form that has a bunch of buttons at the bottom (Save, Cancel, Clear) and these all go in the same cell, with no label for the row.
29
+ #
30
+ # TODO: error messages?
31
+ # @author Alex Chaffee
32
+ class FieldTable < Erector::Widget
33
+
34
+ include Erector::Inline
35
+
36
+ class Field < Erector::Widget
37
+ needs :label, :note => nil
38
+
39
+ def content
40
+ tr :class => "field_table_field" do
41
+ th do
42
+ text @label
43
+ text ":" unless @label.nil?
44
+ end
45
+ td do
46
+ super # calls the block
47
+ end
48
+ if @note
49
+ td do
50
+ text @note
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ # Define a field, containing a label, optional note, and block for the contents
58
+ #
59
+ # TODO: allow passing in a widget instead of a block
60
+ def field(label = nil, note = nil, &contents)
61
+ @fields << Field.new(:label => label, :note => note, &contents)
62
+ end
63
+
64
+ # If you call "button" you can pass in a block that'll get rendered inside the 2nd column of the
65
+ # last row. The idea here is that you might want to make an HTML form that has a bunch of buttons at
66
+ # the bottom (Save, Cancel, Clear) and these all go in the same cell, with no label for the row.
67
+ #
68
+ # TODO: allow passing in a widget instead of a block
69
+ def button(&button_proc)
70
+ @buttons << button_proc
71
+ end
72
+
73
+ needs :title
74
+
75
+ # Pass in a block and it'll get called with a pointer to this table, so you can call
76
+ # 'field' and 'button' to configure it
77
+ def initialize(*args)
78
+ super
79
+ @fields = []
80
+ @buttons = []
81
+ yield self if block_given? # invoke the configuration block
82
+ end
83
+
84
+ def content
85
+ fieldset :class => "field_table" do
86
+ legend @title
87
+ table :width => '100%' do
88
+ @fields.each do |f|
89
+ widget f
90
+ end
91
+ unless @buttons.empty?
92
+ tr :class => "field_table_buttons" do
93
+ td :colspan => 2, :align => "right" do
94
+ table :class => 'layout' do
95
+ tr do
96
+ @buttons.each do |button|
97
+ td :class => "field_table_button" do
98
+ button.call
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ end
@@ -0,0 +1,30 @@
1
+ # todo: make more like http://github.com/justinfrench/formtastic
2
+
3
+ class Form < Erector::Widget
4
+ needs :action, :method => "post"
5
+
6
+ def content
7
+ form :method => form_method, :action => @action do
8
+ unless rest_method == form_method
9
+ input :type => "hidden", :name => "_method", :value => rest_method
10
+ end
11
+ super
12
+ end
13
+ end
14
+
15
+ def method
16
+ @method.to_s.downcase
17
+ end
18
+
19
+ def form_method
20
+ if method == "get"
21
+ "get"
22
+ else
23
+ "post"
24
+ end
25
+ end
26
+
27
+ def rest_method
28
+ method
29
+ end
30
+ end
@@ -0,0 +1,165 @@
1
+ # Erector Page base class.
2
+ #
3
+ # Allows for accumulation of script and style tags (see example below) with
4
+ # either external or inline content. External references are 'uniq'd, so it's
5
+ # a good idea to declare a js script in all widgets that use it, so you don't
6
+ # accidentally lose the script if you remove the one widget that happened to
7
+ # declare it.
8
+ #
9
+ # The script and style declarations are accumulated at class load time, as
10
+ # 'dependencies'. This technique allows all widgets to add their own requirements
11
+ # to the page header without extra logic for declaring which pages include
12
+ # which nested widgets. Fortunately, Page is now smart enough to figure out
13
+ # which widgets were actually rendered during the body_content run, so it only
14
+ # emits into its HEAD the dependencies that are relevant. If it misses some, or
15
+ # if you want to add some extra dependencies -- for instance, styles that apply
16
+ # to widgets that are rendered later via AJAX -- then return an array of those
17
+ # widget classes in your subclass by overriding the #extra_widgets method.
18
+ #
19
+ # If you want something to show up in the headers for just one page type
20
+ # (subclass), then override #head_content, call super, and then emit it
21
+ # yourself.
22
+ #
23
+ # Body content can be supplied in several ways:
24
+ #
25
+ # * In a Page subclass, by overriding the #body_content method:
26
+ #
27
+ # class MyPage < Erector::Widgets::Page
28
+ # def body_content
29
+ # text "body content"
30
+ # end
31
+ # end
32
+ #
33
+ # * Or by overriding #content and passing a block to super:
34
+ #
35
+ # class MyPage < Erector::Widgets::Page
36
+ # def content
37
+ # super do
38
+ # text "body content"
39
+ # end
40
+ # end
41
+ # end
42
+ #
43
+ # * Or by passing a block to Page.new:
44
+ #
45
+ # Erector::Widgets::Page.new do
46
+ # text "body content"
47
+ # end
48
+ #
49
+ # This last trick (passing a block to Page.new) works because Page is an
50
+ # InlineWidget so its block is evaluated in the context of the newly
51
+ # instantiated widget object, and not in the context of its caller. But this
52
+ # means you can't access instance variables of the caller, e.g.
53
+ #
54
+ # @name = "fred"
55
+ # Erector::Widgets::Page.new do
56
+ # text "my name is #{@name}"
57
+ # end
58
+ #
59
+ # will emit "my name is " because @name is nil inside the new Page. However,
60
+ # you *can* call methods in the parent class, thanks to some method_missing
61
+ # magic. Confused? You should be. See Erector::Inline#content for more
62
+ # documentation.
63
+ #
64
+ # Author:: Alex Chaffee, alex@stinky.com
65
+ #
66
+ # = Example Usage:
67
+ #
68
+ # class MyPage < Page
69
+ # external :js, "lib/jquery.js"
70
+ # external :script, "$(document).ready(function(){...});"
71
+ # external :css, "stuff.css"
72
+ # external :style, "li.foo { color: red; }"
73
+ #
74
+ # def page_title
75
+ # "my app"
76
+ # end
77
+ #
78
+ # def body_content
79
+ # h1 "My App"
80
+ # p "welcome to my app"
81
+ # widget WidgetWithExternalStyle
82
+ # end
83
+ # end
84
+ #
85
+ # class WidgetWithExternalStyle < Erector::Widget
86
+ # external :style, "div.custom { border: 2px solid green; }"
87
+ #
88
+ # def content
89
+ # div :class => "custom" do
90
+ # text "green is good"
91
+ # end
92
+ # end
93
+ # end
94
+ #
95
+ # = Thoughts:
96
+ # * It may be desirable to unify #js and #script, and #css and #style, and have the routine be
97
+ # smart enough to analyze its parameter to decide whether to make it a file or a script.
98
+ #
99
+ class Erector::Widgets::Page < Erector::InlineWidget
100
+
101
+ # Emit the Transitional doctype.
102
+ # TODO: allow selection from among different standard doctypes
103
+ def doctype
104
+ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
105
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
106
+ end
107
+
108
+ def content
109
+ extra_head_slot = nil
110
+ rawtext doctype
111
+ html(html_attributes) do
112
+ head do
113
+ head_content
114
+ extra_head_slot = output.placeholder
115
+ end
116
+ body(body_attributes) do
117
+ if block_given?
118
+ yield
119
+ else
120
+ body_content
121
+ end
122
+ end
123
+ end
124
+ # after everything's been rendered, use the placeholder to
125
+ # insert all the head's dependencies
126
+ extra_head_slot << included_head_content
127
+ end
128
+
129
+ # override me to provide a page title (default = name of the Page subclass)
130
+ def page_title
131
+ self.class.name
132
+ end
133
+
134
+ # override me to change the attributes of the HTML element
135
+ def html_attributes
136
+ {:xmlns => 'http://www.w3.org/1999/xhtml', 'xml:lang' => 'en', :lang => 'en'}
137
+ end
138
+
139
+ # override me to add attributes (e.g. a css class) to the body
140
+ def body_attributes
141
+ {}
142
+ end
143
+
144
+ # override me (or instantiate Page with a block)
145
+ def body_content
146
+ call_block
147
+ end
148
+
149
+ # emit the contents of the head element. Override and call super if you want to put more stuff in there.
150
+ def head_content
151
+ meta 'http-equiv' => 'content-type', :content => 'text/html;charset=UTF-8'
152
+ title page_title
153
+ end
154
+
155
+ def included_head_content
156
+ # now that we've rendered the whole page, it's the right time
157
+ # to ask what all widgets were rendered to the output stream
158
+ included_widgets = [self.class] + output.widgets.to_a + extra_widgets
159
+ ExternalRenderer.new(:classes => included_widgets).to_html
160
+ end
161
+
162
+ def extra_widgets
163
+ []
164
+ end
165
+ end