effigy 0.1
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/Rakefile +35 -0
- data/lib/effigy.rb +1 -0
- data/lib/effigy/view.rb +63 -0
- data/spec/effigy/view_spec.rb +78 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/have_selector.rb +68 -0
- metadata +60 -0
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
|
7
|
+
desc 'Default: run the specs.'
|
8
|
+
task :default => [:spec]
|
9
|
+
|
10
|
+
Spec::Rake::SpecTask.new do |t|
|
11
|
+
t.spec_opts = ['--color', '--format', 'progress']
|
12
|
+
t.libs = %w(spec)
|
13
|
+
t.ruby_opts = ['-rrubygems']
|
14
|
+
end
|
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.}
|
21
|
+
|
22
|
+
s.files = FileList['[A-Z]*', 'lib/**/*.rb', 'spec/**/*.rb']
|
23
|
+
s.require_path = 'lib'
|
24
|
+
s.test_files = Dir[*['spec/**/*_spec.rb']]
|
25
|
+
|
26
|
+
s.authors = ["Joe Ferris"]
|
27
|
+
s.email = %q{jferris@thoughtbot.com}
|
28
|
+
|
29
|
+
s.platform = Gem::Platform::RUBY
|
30
|
+
end
|
31
|
+
|
32
|
+
Rake::GemPackageTask.new spec do |pkg|
|
33
|
+
pkg.need_tar = true
|
34
|
+
pkg.need_zip = true
|
35
|
+
end
|
data/lib/effigy.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'effigy/view'
|
data/lib/effigy/view.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Effigy
|
4
|
+
class View
|
5
|
+
def initialize
|
6
|
+
@css_assignments = {}
|
7
|
+
@xpath_assignments = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def text(selector, content)
|
11
|
+
select(selector).content = content
|
12
|
+
end
|
13
|
+
|
14
|
+
def attributes(selector, attributes)
|
15
|
+
element = select(selector)
|
16
|
+
attributes.each do |attribute, value|
|
17
|
+
element[attribute.to_s] = value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
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) }
|
27
|
+
sibling.add_next_sibling(item_element)
|
28
|
+
sibling = item_element
|
29
|
+
end
|
30
|
+
original_element.unlink
|
31
|
+
end
|
32
|
+
|
33
|
+
def render(template)
|
34
|
+
@current_context = Nokogiri::XML.parse(template)
|
35
|
+
yield if block_given?
|
36
|
+
apply
|
37
|
+
current_context.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
def context(new_context)
|
41
|
+
old_context = @current_context
|
42
|
+
@current_context = select(new_context)
|
43
|
+
yield
|
44
|
+
@current_context = old_context
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
attr_reader :current_context
|
50
|
+
|
51
|
+
def select(nodes)
|
52
|
+
if nodes.respond_to?(:search)
|
53
|
+
nodes
|
54
|
+
else
|
55
|
+
current_context.at(nodes)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def apply
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'effigy/view'
|
3
|
+
|
4
|
+
module Effigy
|
5
|
+
describe View do
|
6
|
+
it "should replace element contents" do
|
7
|
+
template = %{<test><element one="abc">something</element></test>}
|
8
|
+
|
9
|
+
view = Effigy::View.new
|
10
|
+
xml = view.render(template) do
|
11
|
+
view.text 'element', 'expected'
|
12
|
+
end
|
13
|
+
|
14
|
+
xml.should have_selector(:element, :contents => 'expected', :one => 'abc')
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should replace element attributes" do
|
18
|
+
template = %{<test><element one="abc">something</element></test>}
|
19
|
+
|
20
|
+
view = Effigy::View.new
|
21
|
+
xml = view.render(template) do
|
22
|
+
view.attributes 'element', :one => '123', :two => '234'
|
23
|
+
end
|
24
|
+
|
25
|
+
xml.should have_selector(:element, :contents => 'something', :one => '123', :two => '234')
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should replace examples" do
|
29
|
+
template = %{<test><element><value>original</value></element></test>}
|
30
|
+
|
31
|
+
view = Effigy::View.new
|
32
|
+
xml = view.render(template) do
|
33
|
+
view.examples_for('element', %w(one two)) do |value|
|
34
|
+
view.text('value', value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
xml.should have_selector('element value', :contents => 'one')
|
39
|
+
xml.should have_selector('element value', :contents => 'two')
|
40
|
+
xml.should_not have_selector('element value', :contents => 'original')
|
41
|
+
xml.should =~ /one.*two/m
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should replace within a context" do
|
45
|
+
template = %{<test><element><value>original</value></element></test>}
|
46
|
+
|
47
|
+
view = Effigy::View.new
|
48
|
+
xml = view.render(template) do
|
49
|
+
view.context('element') do
|
50
|
+
view.text('value', 'expected')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
xml.should have_selector('element value', :contents => 'expected')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe View, "subclass" do
|
59
|
+
before do
|
60
|
+
@subclass = Class.new(View)
|
61
|
+
@subclass.class_eval do
|
62
|
+
def initialize(value)
|
63
|
+
@value = value
|
64
|
+
end
|
65
|
+
|
66
|
+
def apply
|
67
|
+
text('element', @value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should run #apply when rendering" do
|
73
|
+
template = %{<test><element>original</element></test>}
|
74
|
+
view = @subclass.new('expected')
|
75
|
+
view.render(template).should have_selector('element', :contents => 'expected')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec'
|
2
|
+
require 'spec/autorun'
|
3
|
+
|
4
|
+
PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')).freeze
|
5
|
+
|
6
|
+
$: << File.join(PROJECT_ROOT, 'lib')
|
7
|
+
|
8
|
+
Dir[File.join(PROJECT_ROOT, 'spec', 'support', '**', '*.rb')].each { |file| require(file) }
|
9
|
+
|
10
|
+
Spec::Runner.configure do |config|
|
11
|
+
config.include Matchers
|
12
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Matchers
|
2
|
+
class HaveSelector
|
3
|
+
def initialize(selector, attrs)
|
4
|
+
@selector = selector
|
5
|
+
@attrs = attrs
|
6
|
+
@contents = attrs.delete(:contents)
|
7
|
+
end
|
8
|
+
|
9
|
+
def matches?(xml)
|
10
|
+
@xml = xml
|
11
|
+
|
12
|
+
verify_element_present! &&
|
13
|
+
verify_contents! &&
|
14
|
+
verify_attrs!
|
15
|
+
end
|
16
|
+
|
17
|
+
def failure_message
|
18
|
+
"Expected #{@missing},\nGot: #{elements.to_a.join("\n")}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def negative_failure_message
|
22
|
+
"Did not expect #{selector}"
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :xml, :selector, :attrs, :contents, :missing
|
28
|
+
|
29
|
+
def verify_element_present!
|
30
|
+
if elements.empty?
|
31
|
+
@missing = "an element matching the following CSS selector: #{selector}"
|
32
|
+
false
|
33
|
+
else
|
34
|
+
true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def verify_contents!
|
39
|
+
if contents.nil? || elements.any? { |element| element.text == @contents}
|
40
|
+
true
|
41
|
+
else
|
42
|
+
@missing = "element with contents: #{@contents}"
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def verify_attrs!
|
48
|
+
if elements.any? { |element| attrs.all? { |attribute, value| element[attribute.to_s] == value } }
|
49
|
+
true
|
50
|
+
else
|
51
|
+
@missing = "element with attrs: #{attrs.inspect}"
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def elements
|
57
|
+
@elements ||= document.css(selector.to_s)
|
58
|
+
end
|
59
|
+
|
60
|
+
def document
|
61
|
+
@document ||= Nokogiri::XML.parse(xml)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def have_selector(selector, attrs = {})
|
66
|
+
HaveSelector.new(selector, attrs)
|
67
|
+
end
|
68
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: effigy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joe Ferris
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-06 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: 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.
|
17
|
+
email: jferris@thoughtbot.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- Rakefile
|
26
|
+
- lib/effigy/view.rb
|
27
|
+
- lib/effigy.rb
|
28
|
+
- spec/effigy/view_spec.rb
|
29
|
+
- spec/spec_helper.rb
|
30
|
+
- spec/support/have_selector.rb
|
31
|
+
has_rdoc: true
|
32
|
+
homepage:
|
33
|
+
licenses: []
|
34
|
+
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
requirements: []
|
53
|
+
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 1.3.4
|
56
|
+
signing_key:
|
57
|
+
specification_version: 3
|
58
|
+
summary: Effigy provides a view and template framework without a templating language.
|
59
|
+
test_files:
|
60
|
+
- spec/effigy/view_spec.rb
|