effigy 0.1 → 0.2.0

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.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Joe Ferris
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,114 @@
1
+ h1. Effigy
2
+
3
+ Create usable views in Ruby with HTML and CSS selectors.
4
+
5
+ h2. Synopsis
6
+
7
+ In Effigy, your view is a Ruby class that performs transformation on an HTML template. The template is passed to #render, which calls a private #transform method to apply the transformations. The transformed template is then returned as a string of HTML.
8
+
9
+ <pre>
10
+ template = %{
11
+ <html>
12
+ <head>
13
+ <title></title>
14
+ </head>
15
+ <body>
16
+ <h1></h1>
17
+ <p class="body"></p>
18
+ <div class="comment">
19
+ <h2></h2>
20
+ <p></p>
21
+ <a>View more</a>
22
+ </div>
23
+ <p id="no-comments">There aren't any comments for this post.</p>
24
+ </body>
25
+ </html>
26
+ }
27
+
28
+ class PostView < Effigy::View
29
+ attr_reader :post
30
+
31
+ def initialize(post)
32
+ @post = post
33
+ end
34
+
35
+ def transform
36
+ text('h1', post.title)
37
+ text('title', "#{post.title} - Site title")
38
+ text('p.body', post.body)
39
+ replace_with_each('.comment', post.comments) do |comment|
40
+ text('h2', comment.title)
41
+ text('p', comment.summary)
42
+ attributes('a', :href => url_for(comment))
43
+ end
44
+ remove('#no-comments') if post.comments.empty?
45
+ end
46
+ end
47
+
48
+ view = PostView.new(post)
49
+ document = view.render(template)
50
+
51
+ # Result document:
52
+ # <html>
53
+ # <head>
54
+ # <title>Post title - Site title</title>
55
+ # </head>
56
+ # <body>
57
+ # <h1>Post title</h1>
58
+ # <p class="body">Post body</p>
59
+ # <div class="comment">
60
+ # <h2>First comment title</h2>
61
+ # <p>First comment body</p>
62
+ # <a href="/comments/1">View more</a>
63
+ # </div>
64
+ # <div class="comment">
65
+ # <h2>Second comment title</h2>
66
+ # <p>Second comment body</p>
67
+ # <a href="/comments/2">View more</a>
68
+ # </div>
69
+ # </body>
70
+ # </html>
71
+ </pre>
72
+
73
+ See the documentation for more information on available transformations.
74
+
75
+ h2. Rails
76
+
77
+ Effigy integrates with Rails. It provides a view subclass that copies instance variables from the controller, a template handler to find Effigy views and templates, and a generator to create skeleton view files.
78
+
79
+ Example:
80
+
81
+ <pre>
82
+ # app/controllers/magic_controller.rb
83
+ class MagicController < ApplicationController
84
+ def index
85
+ @spell = 'hocus pocus'
86
+ end
87
+ end
88
+ </pre>
89
+
90
+ <pre>
91
+ # app/views/magic/index.html.effigy
92
+ class MagicIndexView < Effigy::Rails::View
93
+ def transform
94
+ text('h1', @spell)
95
+ end
96
+ end
97
+ </pre>
98
+
99
+ <pre>
100
+ # app/templates/magic/index.html
101
+ <h1>Spell name goes here</h1>
102
+ </pre>
103
+
104
+ View this example in your browser and you'll see "hocus pocus."
105
+
106
+ h2. Why?
107
+
108
+ Effigy is based on the idea that putting behavior in your templates is confusing and makes them difficult to maintain, and that the closer an ERB template gets to 50% Ruby, 50% HTML, the closer it gets to total chaos. Complicated views require unintuitive concepts (ERB buffers, capture blocks, etc). ERB also has the constant threat of unescaped user input slipping into a view.
109
+
110
+ Effigy was created because I have never liked interpolation-based templating languages like ERB and because XSLT is a new language (and I like Ruby just fine).
111
+
112
+ h2. Author
113
+
114
+ Effigy was written by Joe Ferris. See LICENSE for license info.
data/Rakefile CHANGED
@@ -4,8 +4,8 @@ require 'rake/gempackagetask'
4
4
 
5
5
  require 'spec/rake/spectask'
6
6
 
7
- desc 'Default: run the specs.'
8
- task :default => [:spec]
7
+ desc 'Default: run the specs and metrics.'
8
+ task :default => [:spec, :metrics]
9
9
 
10
10
  Spec::Rake::SpecTask.new do |t|
11
11
  t.spec_opts = ['--color', '--format', 'progress']
@@ -13,23 +13,79 @@ Spec::Rake::SpecTask.new do |t|
13
13
  t.ruby_opts = ['-rrubygems']
14
14
  end
15
15
 
16
- spec = Gem::Specification.new do |s|
17
- s.name = %q{effigy}
18
- s.version = "0.1"
19
- s.summary = %q{Effigy provides a view and template framework without a templating language.}
20
- s.description = %q{Define views in Ruby and templates in HTML. Avoid code interpolation or ugly templating languages. Use ids, class names, and semantic structures already present in your documents to produce content.}
16
+ task :rails_root do
17
+ rails_root = File.join('tmp', 'rails_root')
18
+ unless File.exist?(rails_root)
19
+ FileUtils.mkdir_p(File.dirname(rails_root))
20
+ command = "rails #{rails_root}"
21
+ output = `#{command} 2>&1`
22
+ if $? == 0
23
+ FileUtils.ln_s(FileUtils.pwd, File.join(rails_root, 'vendor', 'plugins'))
24
+ else
25
+ $stderr.puts "Command failed with status #{$?}:"
26
+ $stderr.puts command
27
+ $stderr.puts output
28
+ end
29
+ end
30
+ end
31
+
32
+
33
+ task :spec => :rails_root
34
+
35
+ desc "Remove build files"
36
+ task :clean do
37
+ %w(tmp pkg doc).each do |path|
38
+ FileUtils.rm_rf(path)
39
+ end
40
+ end
41
+
42
+ begin
43
+ require 'jeweler'
44
+ Jeweler::Tasks.new do |gem|
45
+ gem.name = %q{effigy}
46
+ gem.version = "0.1"
47
+ gem.summary = %q{Effigy provides a view and template framework without a templating language.}
48
+ gem.description = %q{Define views in Ruby and templates in HTML. Avoid code interpolation or ugly templating languages. Use ids, class names, and semantic structures already present in your documents to produce content.}
21
49
 
22
- s.files = FileList['[A-Z]*', 'lib/**/*.rb', 'spec/**/*.rb']
23
- s.require_path = 'lib'
24
- s.test_files = Dir[*['spec/**/*_spec.rb']]
50
+ gem.files = FileList['[A-Z]*', 'lib/**/*.rb', 'spec/**/*.rb']
51
+ gem.require_path = 'lib'
52
+ gem.test_files = Dir[*['spec/**/*_spec.rb']]
25
53
 
26
- s.authors = ["Joe Ferris"]
27
- s.email = %q{jferris@thoughtbot.com}
54
+ gem.authors = ["Joe Ferris"]
55
+ gem.email = %q{jferris@thoughtbot.com}
28
56
 
29
- s.platform = Gem::Platform::RUBY
57
+ gem.platform = Gem::Platform::RUBY
58
+ end
59
+ Jeweler::GemcutterTasks.new
60
+ rescue LoadError
61
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
30
62
  end
31
63
 
32
- Rake::GemPackageTask.new spec do |pkg|
33
- pkg.need_tar = true
34
- pkg.need_zip = true
64
+ begin
65
+ require 'reek/adapters/rake_task'
66
+
67
+ namespace :metrics do
68
+ desc "Run reek"
69
+ Reek::RakeTask.new do |t|
70
+ t.source_files = FileList['lib/**/*.rb', 'rails/**/*.rb']
71
+ t.fail_on_error = false
72
+ end
73
+ end
74
+
75
+ desc "Run all metrics"
76
+ task :metrics => ['metrics:reek']
77
+ rescue LoadError => e
78
+ puts e.inspect
79
+ puts "Missing dependencies for metrics."
80
+ end
81
+
82
+ begin
83
+ require 'yard'
84
+
85
+ YARD::Rake::YardocTask.new do |t|
86
+ t.files = ['lib/**/*.rb', 'rails/**/*.rb']
87
+ end
88
+ rescue LoadError => e
89
+ puts e.inspect
90
+ puts "Missing dependencies for yard."
35
91
  end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -1 +1,3 @@
1
1
  require 'effigy/view'
2
+ require 'effigy/errors'
3
+
@@ -0,0 +1,30 @@
1
+ require 'nokogiri'
2
+
3
+ module Effigy
4
+ class ClassList
5
+ def initialize(element)
6
+ @element = element
7
+ read_class_names
8
+ end
9
+
10
+ def <<(class_name)
11
+ @class_names << class_name
12
+ write_class_names
13
+ end
14
+
15
+ def remove(class_name)
16
+ @class_names.delete(class_name)
17
+ write_class_names
18
+ end
19
+
20
+ private
21
+
22
+ def read_class_names
23
+ @class_names = (@element['class'] || '').split(' ')
24
+ end
25
+
26
+ def write_class_names
27
+ @element['class'] = @class_names.join(' ')
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ module Effigy
2
+ class ElementNotFound < StandardError
3
+ attr_accessor :selector
4
+
5
+ def initialize(selector)
6
+ @selector = selector
7
+ end
8
+
9
+ def message
10
+ "No element matched the given selector: #{selector.inspect}"
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,6 @@
1
+ require 'action_view'
2
+ require 'effigy/view'
3
+ require 'effigy/rails/view'
4
+ require 'effigy/rails/template_handler'
5
+
6
+ ActionView::Template.register_template_handler :effigy, Effigy::Rails::TemplateHandler
@@ -0,0 +1,45 @@
1
+ module Effigy
2
+ module Rails
3
+ class TemplateHandler < ActionView::TemplateHandler
4
+ include ActionView::TemplateHandlers::Compilable
5
+
6
+ def compile(view)
7
+ @view = view
8
+ load_view_class
9
+ return <<-RUBY
10
+ if @controller
11
+ variables = @controller.instance_variable_names
12
+ variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
13
+ assigns = variables.inject({}) do |hash, name|
14
+ hash.update(name => @controller.instance_variable_get(name))
15
+ end
16
+ end
17
+ view = #{view_class_name}.new(assigns) { |*names| yield(*names) }
18
+ view.render(#{template_source.inspect})
19
+ RUBY
20
+ end
21
+
22
+ def view_name
23
+ @view.name
24
+ end
25
+
26
+ def base_path
27
+ @view.base_path
28
+ end
29
+
30
+ def load_view_class
31
+ load(@view.filename)
32
+ end
33
+
34
+ def view_class_name
35
+ [base_path, view_name, 'view'].join('_').camelize
36
+ end
37
+
38
+ def template_source
39
+ template_path = @view.load_path.path.sub(/\/views$/, '/templates')
40
+ template_file_name = File.join(template_path, base_path, "#{view_name}.#{@view.format}")
41
+ IO.read(template_file_name)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,18 @@
1
+ module Effigy
2
+ module Rails
3
+ class View < ::Effigy::View
4
+ def initialize(assigns, &layout_block)
5
+ assigns.each do |name, value|
6
+ instance_variable_set(name, value)
7
+ end
8
+ @layout_block = layout_block
9
+ end
10
+
11
+ protected
12
+
13
+ def content_for(capture)
14
+ @layout_block.call(capture)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,12 +1,9 @@
1
1
  require 'nokogiri'
2
+ require 'effigy/class_list'
3
+ require 'effigy/errors'
2
4
 
3
5
  module Effigy
4
6
  class View
5
- def initialize
6
- @css_assignments = {}
7
- @xpath_assignments = {}
8
- end
9
-
10
7
  def text(selector, content)
11
8
  select(selector).content = content
12
9
  end
@@ -18,14 +15,11 @@ module Effigy
18
15
  end
19
16
  end
20
17
 
21
- def examples_for(selector, collection, &block)
22
- original_element = current_context.at(selector)
23
- sibling = original_element
24
- collection.each do |item|
25
- item_element = original_element.dup
26
- context(item_element) { yield(item) }
18
+ def replace_with_each(selector, collection, &block)
19
+ original_element = select(selector)
20
+ collection.inject(original_element) do |sibling, item|
21
+ item_element = clone_element_with_item(original_element, item, &block)
27
22
  sibling.add_next_sibling(item_element)
28
- sibling = item_element
29
23
  end
30
24
  original_element.unlink
31
25
  end
@@ -33,8 +27,8 @@ module Effigy
33
27
  def render(template)
34
28
  @current_context = Nokogiri::XML.parse(template)
35
29
  yield if block_given?
36
- apply
37
- current_context.to_s
30
+ transform
31
+ output
38
32
  end
39
33
 
40
34
  def context(new_context)
@@ -44,19 +38,60 @@ module Effigy
44
38
  @current_context = old_context
45
39
  end
46
40
 
41
+ def remove(selector)
42
+ select_all(selector).each { |element| element.unlink }
43
+ end
44
+
45
+ def add_class_names(selector, *class_names)
46
+ element = select(selector)
47
+ class_list = ClassList.new(element)
48
+ class_names.each { |class_name| class_list << class_name }
49
+ end
50
+
51
+ def remove_class_names(selector, *class_names)
52
+ element = select(selector)
53
+ class_list = ClassList.new(element)
54
+ class_names.each { |class_name| class_list.remove(class_name) }
55
+ end
56
+
57
+ def inner(selector, xml)
58
+ select(selector).inner_html = xml
59
+ end
60
+
61
+ def outer(selector, xml)
62
+ select(selector).after(xml).unlink
63
+ end
64
+
47
65
  private
48
66
 
67
+ def transform
68
+ end
69
+
49
70
  attr_reader :current_context
50
71
 
51
72
  def select(nodes)
52
73
  if nodes.respond_to?(:search)
53
74
  nodes
54
75
  else
55
- current_context.at(nodes)
76
+ current_context.at(nodes) or
77
+ raise ElementNotFound, nodes
56
78
  end
57
79
  end
58
80
 
59
- def apply
81
+ def select_all(selector)
82
+ result = current_context.search(selector)
83
+ raise ElementNotFound, selector if result.empty?
84
+ result
85
+ end
86
+
87
+ def clone_element_with_item(original_element, item, &block)
88
+ item_element = original_element.dup
89
+ context(item_element) { yield(item) }
90
+ item_element
91
+ end
92
+
93
+ def output
94
+ current_context.to_xhtml
60
95
  end
61
96
 
62
97
  end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+ require 'effigy/class_list'
3
+
4
+ describe Effigy::ClassList do
5
+
6
+ it "should add a class name to an element with one existing class name" do
7
+ element = Nokogiri::XML.parse(%{<test class="original"/>}).at("//test")
8
+ class_list = Effigy::ClassList.new(element)
9
+ class_list << 'another'
10
+ element.should have_selector('test.original')
11
+ element.should have_selector('test.another')
12
+ end
13
+
14
+ it "should add a class name to an element with two existing class names" do
15
+ element = Nokogiri::XML.parse(%{<test class="one two"/>}).at("//test")
16
+ class_list = Effigy::ClassList.new(element)
17
+ class_list << 'another'
18
+ element.should have_selector('test.one')
19
+ element.should have_selector('test.two')
20
+ element.should have_selector('test.another')
21
+ end
22
+
23
+ it "should add a class name to an element without a class attribute" do
24
+ element = Nokogiri::XML.parse(%{<test/>}).at("//test")
25
+ class_list = Effigy::ClassList.new(element)
26
+ class_list << 'another'
27
+ element.should have_selector('test.another')
28
+ end
29
+
30
+ it "should remove a class name from an element with one existing class name" do
31
+ element = Nokogiri::XML.parse(%{<test class="original"/>}).at("//test")
32
+ class_list = Effigy::ClassList.new(element)
33
+ class_list.remove 'original'
34
+ element.should_not have_selector('test.original')
35
+ end
36
+
37
+ it "should remove a class name from an element with two existing class names" do
38
+ element = Nokogiri::XML.parse(%{<test class="one two"/>}).at("//test")
39
+ class_list = Effigy::ClassList.new(element)
40
+ class_list.remove 'one'
41
+ element.should have_selector('test.two')
42
+ element.should_not have_selector('test.one')
43
+ end
44
+
45
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require 'effigy/errors'
3
+
4
+ describe Effigy::ElementNotFound do
5
+ it "should accept a selector and use it in the message" do
6
+ error = Effigy::ElementNotFound.new('user.selected')
7
+ error.selector.should == 'user.selected'
8
+ error.message.should include('user.selected')
9
+ end
10
+ end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ require File.join(RAILS_ROOT, 'config', 'environment')
4
+ require File.join(PROJECT_ROOT, 'rails', 'init')
5
+ require 'action_view/test_case'
6
+ require 'nokogiri'
7
+
8
+ describe "a controller with an effigy view and template" do
9
+ before do
10
+ @files = []
11
+ create_rails_source_file 'app/controllers/magic_controller.rb', <<-RUBY
12
+ class MagicController < ApplicationController
13
+ layout 'application'
14
+ include ActionController::TestCase::RaiseActionExceptions
15
+ def index
16
+ @spell = 'hocus pocus'
17
+ render
18
+ end
19
+ end
20
+ RUBY
21
+
22
+ create_rails_file 'app/views/magic/index.html.effigy', <<-RUBY
23
+ class MagicIndexView < Effigy::Rails::View
24
+ def transform
25
+ text('h1', @spell)
26
+ end
27
+ end
28
+ RUBY
29
+
30
+ create_rails_file 'app/templates/magic/index.html', <<-HTML
31
+ <h1 class="success">placeholder title</h1>
32
+ HTML
33
+
34
+ create_rails_file 'app/views/layouts/application.html.effigy', <<-RUBY
35
+ class LayoutsApplicationView < Effigy::Rails::View
36
+ def transform
37
+ inner('body', content_for(:layout))
38
+ end
39
+ end
40
+ RUBY
41
+
42
+ create_rails_file 'app/templates/layouts/application.html', <<-HTML
43
+ <html><body></body></html>
44
+ HTML
45
+
46
+ @controller = MagicController.new
47
+ @request = ActionController::TestRequest.new
48
+ @response = ActionController::TestResponse.new
49
+ end
50
+
51
+ after do
52
+ @files.each do |file|
53
+ FileUtils.rm(file)
54
+ end
55
+ end
56
+
57
+ def create_rails_file(relative_path, contents)
58
+ absolute_path = File.join(RAILS_ROOT, relative_path)
59
+ FileUtils.mkdir_p(File.dirname(absolute_path))
60
+ File.open(absolute_path, 'w') { |file| file.write(contents) }
61
+ @files << absolute_path
62
+ absolute_path
63
+ end
64
+
65
+ def create_rails_source_file(relative_path, contents)
66
+ load create_rails_file(relative_path, contents)
67
+ end
68
+
69
+ def render
70
+ get :index
71
+ end
72
+
73
+ include ActionController::TestProcess
74
+
75
+ it "should use the view to render the template" do
76
+ render
77
+ @response.should be_success
78
+ @response.rendered[:template].to_s.should == 'magic/index.html.effigy'
79
+ assigns(:spell).should_not be_nil
80
+ @response.body.should have_selector('h1.success', :contents => assigns(:spell))
81
+ end
82
+
83
+ it "should render an effigy layout" do
84
+ render
85
+
86
+ @response.should be_success
87
+ @response.body.should have_selector('html body h1.success')
88
+ end
89
+ end
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'effigy/view'
3
+ require 'effigy/errors'
3
4
 
4
5
  module Effigy
5
6
  describe View do
@@ -25,12 +26,12 @@ module Effigy
25
26
  xml.should have_selector(:element, :contents => 'something', :one => '123', :two => '234')
26
27
  end
27
28
 
28
- it "should replace examples" do
29
+ it "should replace an element with a clone for each item in a collection" do
29
30
  template = %{<test><element><value>original</value></element></test>}
30
31
 
31
32
  view = Effigy::View.new
32
33
  xml = view.render(template) do
33
- view.examples_for('element', %w(one two)) do |value|
34
+ view.replace_with_each('element', %w(one two)) do |value|
34
35
  view.text('value', value)
35
36
  end
36
37
  end
@@ -53,6 +54,107 @@ module Effigy
53
54
 
54
55
  xml.should have_selector('element value', :contents => 'expected')
55
56
  end
57
+
58
+ it "should remove all matching elements" do
59
+ template = %{<test><first class="yes"/><other class="yes"/><last class="no"/></test>}
60
+
61
+ view = Effigy::View.new
62
+ xml = view.render(template) do
63
+ view.remove('.yes')
64
+ end
65
+
66
+ xml.should have_selector('.no')
67
+ xml.should_not have_selector('.yes')
68
+ end
69
+
70
+ it "should add the given class names" do
71
+ template = %{<test class="original"/>}
72
+
73
+ view = Effigy::View.new
74
+ xml = view.render(template) do
75
+ view.add_class_names('test', 'one', 'two')
76
+ end
77
+
78
+ xml.should have_selector('test.original')
79
+ xml.should have_selector('test.one')
80
+ xml.should have_selector('test.two')
81
+ end
82
+
83
+ it "should remove the given class names" do
84
+ template = %{<test class="one two three"/>}
85
+
86
+ view = Effigy::View.new
87
+ xml = view.render(template) do
88
+ view.remove_class_names('test', 'one', 'two')
89
+ end
90
+
91
+ xml.should have_selector('test.three')
92
+ xml.should_not have_selector('test.one')
93
+ xml.should_not have_selector('test.two')
94
+ end
95
+
96
+ it "should replace an element's inner markup" do
97
+ template = %{<test><original>contents</original></test>}
98
+
99
+ view = Effigy::View.new
100
+ xml = view.render(template) do
101
+ view.inner 'test', '<new>replaced</new>'
102
+ end
103
+
104
+ xml.should have_selector('test new', :contents => 'replaced')
105
+ xml.should_not have_selector('original')
106
+ end
107
+
108
+ it "should replace an element's outer markup" do
109
+ template = %{<test><original>contents</original></test>}
110
+
111
+ view = Effigy::View.new
112
+ xml = view.render(template) do
113
+ view.outer 'test', '<new>replaced</new>'
114
+ end
115
+
116
+ xml.should have_selector('new', :contents => 'replaced')
117
+ xml.should_not have_selector('test')
118
+ end
119
+
120
+ it "should render xhtml by default" do
121
+ template = %{<html/>}
122
+ xml = Effigy::View.new.render(template)
123
+ xml.should_not include('xml')
124
+ end
125
+
126
+ describe "given a template without .find" do
127
+ def render(&block)
128
+ lambda do
129
+ view = Effigy::View.new
130
+ view.render('<test/>') { block.call(view) }
131
+ end
132
+ end
133
+
134
+ it "should raise when updating text content for .find" do
135
+ render { |view| view.text('.find', 'value') }.should raise_error(Effigy::ElementNotFound)
136
+ end
137
+
138
+ it "should raise when updating attributes for .find" do
139
+ render { |view| view.attributes('.find', :attr => 'value') }.
140
+ should raise_error(Effigy::ElementNotFound)
141
+ end
142
+
143
+ it "should raise when replacing an element matching .find" do
144
+ render { |view| view.replace_with_each('.find', []) }.
145
+ should raise_error(Effigy::ElementNotFound)
146
+ end
147
+
148
+ it "should raise when removing elements matching .find" do
149
+ render { |view| view.remove('.find') }.
150
+ should raise_error(Effigy::ElementNotFound)
151
+ end
152
+
153
+ it "should raise when setting the context to .find" do
154
+ render { |view| view.context('.find') }.
155
+ should raise_error(Effigy::ElementNotFound)
156
+ end
157
+ end
56
158
  end
57
159
 
58
160
  describe View, "subclass" do
@@ -63,13 +165,13 @@ module Effigy
63
165
  @value = value
64
166
  end
65
167
 
66
- def apply
168
+ def transform
67
169
  text('element', @value)
68
170
  end
69
171
  end
70
172
  end
71
173
 
72
- it "should run #apply when rendering" do
174
+ it "should run #transform when rendering" do
73
175
  template = %{<test><element>original</element></test>}
74
176
  view = @subclass.new('expected')
75
177
  view.render(template).should have_selector('element', :contents => 'expected')
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe "script/generate effigy_view users create" do
4
+ before do
5
+ @controller_name = 'users'
6
+ @view_name = 'create'
7
+ @view_class_name = 'UsersCreateView'
8
+ FileUtils.cd RAILS_ROOT do
9
+ command = "script/generate effigy_view --backtrace #{@controller_name} #{@view_name} 2>&1"
10
+ output = `#{command}`
11
+ unless $? == 0
12
+ violated "Command failed: #{command}\n#{output}"
13
+ end
14
+ end
15
+ end
16
+
17
+ after do
18
+ FileUtils.rm_f(view_path)
19
+ FileUtils.rm_f(template_path)
20
+ end
21
+
22
+ it "should create a view file" do
23
+ view_path.should contain("class #{@view_class_name} < Rails::Effigy::View")
24
+ view_path.should contain("private")
25
+ view_path.should contain("def transform")
26
+ view_path.should contain(relative_template_path)
27
+ view_path.should contain("end\nend")
28
+ end
29
+
30
+ it "should create a template file" do
31
+ template_path.should contain("<h1>#{@view_class_name}</h1>")
32
+ template_path.should contain("<p>Edit me at #{relative_template_path}</p>")
33
+ template_path.should contain("<p>Edit my view at #{relative_view_path}</p>")
34
+ end
35
+
36
+ def view_path
37
+ File.join(RAILS_ROOT, relative_view_path)
38
+ end
39
+
40
+ def relative_view_path
41
+ File.join('app', 'views', @controller_name, "#{@view_name}.html.effigy")
42
+ end
43
+
44
+ def template_path
45
+ File.join(RAILS_ROOT, relative_template_path)
46
+ end
47
+
48
+ def relative_template_path
49
+ File.join('app', 'templates', @controller_name, "#{@view_name}.html")
50
+ end
51
+
52
+ def contain(expected_text)
53
+ simple_matcher("contain the following lines:\n#{expected_text}") do |path, matcher|
54
+ if File.exist?(path)
55
+ actual_text = IO.read(path)
56
+ if actual_text.include?(expected_text)
57
+ true
58
+ else
59
+ matcher.failure_message =
60
+ "Expected to get the following text:\n#{expected_text}\nBut got:\n#{actual_text}"
61
+ false
62
+ end
63
+ else
64
+ matcher.failure_message = "File does not exist"
65
+ false
66
+ end
67
+ end
68
+ end
69
+
70
+ end
@@ -2,6 +2,7 @@ require 'spec'
2
2
  require 'spec/autorun'
3
3
 
4
4
  PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')).freeze
5
+ RAILS_ROOT = File.join(PROJECT_ROOT, 'tmp', 'rails_root')
5
6
 
6
7
  $: << File.join(PROJECT_ROOT, 'lib')
7
8
 
@@ -7,7 +7,7 @@ module Matchers
7
7
  end
8
8
 
9
9
  def matches?(xml)
10
- @xml = xml
10
+ @xml = xml.to_s
11
11
 
12
12
  verify_element_present! &&
13
13
  verify_contents! &&
@@ -15,11 +15,11 @@ module Matchers
15
15
  end
16
16
 
17
17
  def failure_message
18
- "Expected #{@missing},\nGot: #{elements.to_a.join("\n")}"
18
+ "Expected #{@missing},\nGot: #{@xml}"
19
19
  end
20
20
 
21
21
  def negative_failure_message
22
- "Did not expect #{selector}"
22
+ "Did not expect #{selector},\nGot: #{@xml}"
23
23
  end
24
24
 
25
25
  private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effigy
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.1"
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Ferris
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-06 00:00:00 -04:00
12
+ date: 2009-11-03 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -19,13 +19,26 @@ executables: []
19
19
 
20
20
  extensions: []
21
21
 
22
- extra_rdoc_files: []
23
-
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.textile
24
25
  files:
26
+ - LICENSE
27
+ - README.textile
25
28
  - Rakefile
26
- - lib/effigy/view.rb
29
+ - VERSION
27
30
  - lib/effigy.rb
31
+ - lib/effigy/class_list.rb
32
+ - lib/effigy/errors.rb
33
+ - lib/effigy/rails.rb
34
+ - lib/effigy/rails/template_handler.rb
35
+ - lib/effigy/rails/view.rb
36
+ - lib/effigy/view.rb
37
+ - spec/effigy/class_list_spec.rb
38
+ - spec/effigy/errors_spec.rb
39
+ - spec/effigy/rails/template_handler_spec.rb
28
40
  - spec/effigy/view_spec.rb
41
+ - spec/rails/generators/effigy_view_spec.rb
29
42
  - spec/spec_helper.rb
30
43
  - spec/support/have_selector.rb
31
44
  has_rdoc: true
@@ -33,8 +46,8 @@ homepage:
33
46
  licenses: []
34
47
 
35
48
  post_install_message:
36
- rdoc_options: []
37
-
49
+ rdoc_options:
50
+ - --charset=UTF-8
38
51
  require_paths:
39
52
  - lib
40
53
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -52,9 +65,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
52
65
  requirements: []
53
66
 
54
67
  rubyforge_project:
55
- rubygems_version: 1.3.4
68
+ rubygems_version: 1.3.5
56
69
  signing_key:
57
70
  specification_version: 3
58
71
  summary: Effigy provides a view and template framework without a templating language.
59
72
  test_files:
73
+ - spec/effigy/class_list_spec.rb
74
+ - spec/effigy/errors_spec.rb
75
+ - spec/effigy/rails/template_handler_spec.rb
60
76
  - spec/effigy/view_spec.rb
77
+ - spec/rails/generators/effigy_view_spec.rb