effigy 0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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