active_api 0.1.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/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +174 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/active_api.gemspec +73 -0
- data/geminstaller.yml +15 -0
- data/lib/active_api.rb +14 -0
- data/lib/active_api/builder.rb +11 -0
- data/lib/active_api/collection.rb +30 -0
- data/lib/active_api/complex_type.rb +58 -0
- data/lib/active_api/definition.rb +74 -0
- data/lib/active_api/field.rb +25 -0
- data/lib/active_api/has_definition.rb +9 -0
- data/lib/active_api/schema.rb +39 -0
- data/lib/active_api/simple_type.rb +103 -0
- data/spec/active_api/complex_type_spec.rb +27 -0
- data/spec/active_api/definition_spec.rb +31 -0
- data/spec/active_api/field_spec.rb +15 -0
- data/spec/active_api/schema_spec.rb +59 -0
- data/spec/active_api/simple_type_spec.rb +13 -0
- data/spec/api_spec.rb +367 -0
- data/spec/domain.rb +42 -0
- data/spec/spec_helper.rb +21 -0
- metadata +106 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
module ActiveApi
|
2
|
+
class Definition
|
3
|
+
attr_reader :definition_name, :fields, :builder_class
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
@definition_name = options[:definition_name]
|
7
|
+
@builder_class = options[:builder_class] || ComplexType.to_s
|
8
|
+
@fields = options[:fields] || []
|
9
|
+
end
|
10
|
+
|
11
|
+
def attribute(name, type = :string, options = {})
|
12
|
+
field options.merge(:name => name, :type => type, :field_type => :attribute)
|
13
|
+
end
|
14
|
+
|
15
|
+
def element(name, type = :string, options = {})
|
16
|
+
send type, name, options
|
17
|
+
end
|
18
|
+
|
19
|
+
SimpleType.formats.each do |hash|
|
20
|
+
hash.each do |standard_name, xml_name|
|
21
|
+
define_method standard_name do |name, *options|
|
22
|
+
options = options.first || {}
|
23
|
+
field options.merge(:name => name, :type => standard_name, :klass => SimpleType)
|
24
|
+
end
|
25
|
+
|
26
|
+
if standard_name != xml_name
|
27
|
+
define_method xml_name do |name, *options|
|
28
|
+
options = options.first || {}
|
29
|
+
field options.merge(:name => name, :type => standard_name, :klass => SimpleType)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def belongs_to(name, options = {})
|
36
|
+
field options.merge(:name => name, :type => :belongs_to, :klass => ComplexType)
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_one(name, options = {})
|
40
|
+
field options.merge(:name => name, :type => :has_one, :klass => ComplexType)
|
41
|
+
end
|
42
|
+
|
43
|
+
def has_many(name, options = {})
|
44
|
+
field options.merge(:name => name, :type => :has_many, :klass => Collection)
|
45
|
+
end
|
46
|
+
|
47
|
+
def attributes
|
48
|
+
fields.select do |field|
|
49
|
+
field.field_type == :attribute
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def elements
|
54
|
+
fields.select do |field|
|
55
|
+
field.field_type == :element
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def singular_name
|
60
|
+
name.to_s.singularize
|
61
|
+
end
|
62
|
+
|
63
|
+
def plural_name
|
64
|
+
name.to_s.pluralize
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def field(options)
|
70
|
+
self.fields << Field.new(options)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ActiveApi
|
2
|
+
class Field
|
3
|
+
attr_reader :type, :name, :klass, :value, :choice, :field_type
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@type = options[:type]
|
7
|
+
@name = options[:name]
|
8
|
+
@klass = options[:klass]
|
9
|
+
@value = options[:value]
|
10
|
+
@choice = options[:choice]
|
11
|
+
@field_type = options[:field_type] || :element
|
12
|
+
end
|
13
|
+
|
14
|
+
def name_for(object)
|
15
|
+
if choice
|
16
|
+
value = object.send(name)
|
17
|
+
return nil if value.nil?
|
18
|
+
return choice[value.class.to_s]
|
19
|
+
else
|
20
|
+
name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module ActiveApi
|
2
|
+
|
3
|
+
class Schema
|
4
|
+
class_inheritable_array :versions
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def version(version, options = {})
|
8
|
+
options[:definition_class] ||= Definition
|
9
|
+
schema = Schema.new version, options
|
10
|
+
yield schema
|
11
|
+
write_inheritable_array :versions, [schema]
|
12
|
+
schema
|
13
|
+
end
|
14
|
+
|
15
|
+
def find(version)
|
16
|
+
versions.detect { |schema| schema.version == version }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :version, :definitions, :definition_class
|
21
|
+
def initialize(version, options = {})
|
22
|
+
@version = version
|
23
|
+
@definitions = []
|
24
|
+
@definition_class = options[:definition_class]
|
25
|
+
end
|
26
|
+
|
27
|
+
def define(name, options = {})
|
28
|
+
options[:definition_name] = name
|
29
|
+
definition = definition_class.to_s.constantize.new options
|
30
|
+
yield definition if block_given?
|
31
|
+
definitions << definition
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_xml(objects, options)
|
35
|
+
Collection.new(objects, options.merge(:schema => self)).build_xml
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module ActiveApi
|
2
|
+
class SimpleType
|
3
|
+
include Builder
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def formats
|
7
|
+
standard_names = [
|
8
|
+
:base64Binary,
|
9
|
+
:boolean,
|
10
|
+
:date,
|
11
|
+
:dateTime,
|
12
|
+
:dateTimeStamp,
|
13
|
+
:decimal,
|
14
|
+
:integer,
|
15
|
+
:long,
|
16
|
+
:int,
|
17
|
+
:short,
|
18
|
+
:byte,
|
19
|
+
:nonNegativeInteger,
|
20
|
+
:positiveInteger,
|
21
|
+
:unsignedLong,
|
22
|
+
:unsignedInt,
|
23
|
+
:unsignedShort,
|
24
|
+
:unsignedByte,
|
25
|
+
:nonPositiveInteger,
|
26
|
+
:negativeInteger,
|
27
|
+
:double,
|
28
|
+
:duration,
|
29
|
+
:dayTimeDuration,
|
30
|
+
:yearMonthDuration,
|
31
|
+
:float,
|
32
|
+
:gDay,
|
33
|
+
:gMonth,
|
34
|
+
:gMonthDay,
|
35
|
+
:gYear,
|
36
|
+
:gYearMonth,
|
37
|
+
:hexBinary,
|
38
|
+
:precisionDecimal,
|
39
|
+
:string,
|
40
|
+
:normalizedString,
|
41
|
+
:token,
|
42
|
+
:language,
|
43
|
+
:time
|
44
|
+
].map do |format|
|
45
|
+
{format.to_s.underscore.to_sym => format}
|
46
|
+
end
|
47
|
+
|
48
|
+
custom_names = [
|
49
|
+
{:any_uri => :anyURI},
|
50
|
+
{:notation => :NOTATION},
|
51
|
+
{:qname => :QName},
|
52
|
+
{:name => :Name},
|
53
|
+
{:nc_name => :NCName},
|
54
|
+
{:entity => :ENTITY},
|
55
|
+
{:id => :ID},
|
56
|
+
{:idref => :IDREF},
|
57
|
+
{:nmtoken => :NMTOKEN},
|
58
|
+
]
|
59
|
+
|
60
|
+
standard_names + custom_names
|
61
|
+
end
|
62
|
+
|
63
|
+
def default_format_proc
|
64
|
+
proc { |value| value }
|
65
|
+
end
|
66
|
+
|
67
|
+
def format_procs
|
68
|
+
{
|
69
|
+
:normalized_string => proc {|value| value },
|
70
|
+
:token => proc {|value| value },
|
71
|
+
:date_time => proc {|value| value.strftime("%Y-%m-%dT%H:%M:%S%Z") },
|
72
|
+
:time => proc {|value| value.strftime("%H:%M:%S%Z") },
|
73
|
+
:date => proc {|value| value.strftime("%Y-%m-%d") },
|
74
|
+
:any_uri => proc {|value| URI.escape(value) }
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
attr_reader :text, :node, :format
|
80
|
+
|
81
|
+
def initialize(text, options)
|
82
|
+
@text = text
|
83
|
+
@node = options[:node]
|
84
|
+
@format = options[:format]
|
85
|
+
end
|
86
|
+
|
87
|
+
def append(hash)
|
88
|
+
hash[node] = formatted_text
|
89
|
+
end
|
90
|
+
|
91
|
+
protected
|
92
|
+
|
93
|
+
def build(builder)
|
94
|
+
builder.send "#{node}_", formatted_text
|
95
|
+
end
|
96
|
+
|
97
|
+
def formatted_text
|
98
|
+
return nil if text.blank?
|
99
|
+
(self.class.format_procs[format] || self.class.default_format_proc).call(text)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
module ActiveApi
|
4
|
+
describe ComplexType do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@article = Article.new
|
8
|
+
@article.id = 1
|
9
|
+
@article.title = "Some title"
|
10
|
+
|
11
|
+
@schema = Schema.version(:v1) do |xsl|
|
12
|
+
xsl.define :article do |t|
|
13
|
+
t.string :title
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "with a definition with fields" do
|
19
|
+
it "emits the node and all fields within the node" do
|
20
|
+
element = ComplexType.new @article, :node => :article, :schema => @schema
|
21
|
+
doc = element.build_xml.doc
|
22
|
+
doc.xpath("/article/title").first.inner_text.should == @article.title
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
module ActiveApi
|
4
|
+
describe Definition do
|
5
|
+
|
6
|
+
describe "#class_symbol" do
|
7
|
+
it "returns what is passed in" do
|
8
|
+
Definition.new(:definition_name => :article).definition_name.should == :article
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#fields" do
|
13
|
+
it "is empty by default" do
|
14
|
+
Definition.new(:definition_name => :article).fields.should be_empty
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
[:string, :has_many, :belongs_to, :has_one].each do |method_name|
|
19
|
+
describe "##{method_name}" do
|
20
|
+
it "adds a #{method_name} field with the options" do
|
21
|
+
definition = Definition.new(:definition_name => :article)
|
22
|
+
definition.send method_name, :title
|
23
|
+
definition.fields.length.should == 1
|
24
|
+
definition.fields.first.type.should == method_name
|
25
|
+
definition.fields.first.name.should == :title
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
module ActiveApi
|
4
|
+
describe Field do
|
5
|
+
it "has a type" do
|
6
|
+
definition = Field.new :type => :string
|
7
|
+
definition.type.should == :string
|
8
|
+
end
|
9
|
+
|
10
|
+
it "has a name" do
|
11
|
+
definition = Field.new :name => :title
|
12
|
+
definition.name.should == :title
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
module ActiveApi
|
4
|
+
describe Schema do
|
5
|
+
|
6
|
+
before do
|
7
|
+
Schema.version(:v1) do |schema|
|
8
|
+
schema.define :article do |t|
|
9
|
+
t.string :title
|
10
|
+
t.has_many :comments
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ".define" do
|
16
|
+
describe "with a single field" do
|
17
|
+
|
18
|
+
it "adds one definition of the correct type" do
|
19
|
+
schema = Schema.find(:v1)
|
20
|
+
schema.definitions.length.should == 1
|
21
|
+
definition = schema.definitions.first
|
22
|
+
definition.should be_kind_of(Definition)
|
23
|
+
definition.definition_name.should == :article
|
24
|
+
end
|
25
|
+
|
26
|
+
it "sets the fields on the definition correctly" do
|
27
|
+
definition = Schema.find(:v1).definitions.first
|
28
|
+
definition.fields.length.should == 2
|
29
|
+
field = definition.fields.first
|
30
|
+
field.type.should == :string
|
31
|
+
field.name.should == :title
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "with a multiple fields" do
|
36
|
+
|
37
|
+
it "adds one definition of the correct type article" do
|
38
|
+
schema = Schema.find(:v1)
|
39
|
+
schema.definitions.length.should == 1
|
40
|
+
definition = schema.definitions.first
|
41
|
+
definition.should be_kind_of(Definition)
|
42
|
+
definition.definition_name.should == :article
|
43
|
+
end
|
44
|
+
|
45
|
+
it "sets the fields on the definition correctly" do
|
46
|
+
definition = Schema.find(:v1).definitions.first
|
47
|
+
definition.fields.length.should == 2
|
48
|
+
field1 = definition.fields.first
|
49
|
+
field1.type.should == :string
|
50
|
+
field1.name.should == :title
|
51
|
+
field2 = definition.fields.last
|
52
|
+
field2.type.should == :has_many
|
53
|
+
field2.name.should == :comments
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
module ActiveApi
|
4
|
+
describe SimpleType do
|
5
|
+
|
6
|
+
it "builds the node with the value" do
|
7
|
+
element = SimpleType.new "foo", :node => :bar
|
8
|
+
doc = element.build_xml.doc
|
9
|
+
doc.xpath("/bar").first.inner_text.should == "foo"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
data/spec/api_spec.rb
ADDED
@@ -0,0 +1,367 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
module ActiveApi
|
4
|
+
describe "Defining a schema" do
|
5
|
+
|
6
|
+
describe "calling a collection from a schema" do
|
7
|
+
before do
|
8
|
+
@schema = Schema.version(:v1) do |xsl|
|
9
|
+
xsl.define :article do |t|
|
10
|
+
t.element :id, :string
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@article = Article.new :id => 456
|
14
|
+
end
|
15
|
+
|
16
|
+
it "emits the string element" do
|
17
|
+
element = Schema.find(:v1).build_xml [@article], :node => :article
|
18
|
+
doc = element.doc
|
19
|
+
doc.xpath("/articles/article/id").inner_text.should == "456"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "with an element of type string" do
|
24
|
+
before do
|
25
|
+
@schema = Schema.version(:v1) do |xsl|
|
26
|
+
xsl.define :article do |t|
|
27
|
+
t.element :id, :string
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@article = Article.new :id => 456
|
31
|
+
end
|
32
|
+
|
33
|
+
it "emits the string element" do
|
34
|
+
element = Collection.new [@article], :node => :article, :schema => @schema
|
35
|
+
doc = element.build_xml.doc
|
36
|
+
doc.xpath("/articles/article/id").inner_text.should == "456"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "with a string" do
|
41
|
+
before do
|
42
|
+
@schema = Schema.version(:v1) do |xsl|
|
43
|
+
xsl.define :article do |t|
|
44
|
+
t.string :title
|
45
|
+
end
|
46
|
+
end
|
47
|
+
@article = Article.new :title => Faker::Company.bs
|
48
|
+
end
|
49
|
+
|
50
|
+
it "emits the string element" do
|
51
|
+
element = Collection.new [@article], :node => :article, :schema => @schema
|
52
|
+
doc = element.build_xml.doc
|
53
|
+
doc.xpath("/articles/article/title").first.inner_text.should == @article.title
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "with an attribute of type string" do
|
58
|
+
before do
|
59
|
+
@schema = Schema.version(:v1) do |xsl|
|
60
|
+
xsl.define :article do |t|
|
61
|
+
t.attribute :id
|
62
|
+
end
|
63
|
+
end
|
64
|
+
@article = Article.new :id => 456
|
65
|
+
end
|
66
|
+
|
67
|
+
it "emits the string attribute" do
|
68
|
+
element = Collection.new [@article], :node => :article, :schema => @schema
|
69
|
+
doc = element.build_xml.doc
|
70
|
+
doc.xpath("/articles/article[@id=456]").should be
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "with other data types" do
|
75
|
+
before do
|
76
|
+
@article = Article.new :published_on => Date.parse("3/5/1956")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "emits the correctly formatted element" do
|
80
|
+
@schema = Schema.version(:v1) do |xsl|
|
81
|
+
xsl.define :article do |t|
|
82
|
+
t.date :published_on
|
83
|
+
end
|
84
|
+
end
|
85
|
+
element = Collection.new [@article], :node => :article, :schema => @schema
|
86
|
+
doc = element.build_xml.doc
|
87
|
+
doc.xpath("/articles/article/published_on").first.inner_text.should == "1956-03-05"
|
88
|
+
end
|
89
|
+
|
90
|
+
it "emits nil when the value is nil" do
|
91
|
+
@schema = Schema.version(:v1) do |xsl|
|
92
|
+
xsl.define :article do |t|
|
93
|
+
t.date :published_on
|
94
|
+
end
|
95
|
+
end
|
96
|
+
@article.published_on = nil
|
97
|
+
element = Collection.new [@article], :node => :article, :schema => @schema
|
98
|
+
doc = element.build_xml.doc
|
99
|
+
doc.xpath("/articles/article/published_on").first.inner_text.should == ""
|
100
|
+
end
|
101
|
+
|
102
|
+
it "emits the correctly formatted attribute" do
|
103
|
+
@schema = Schema.version(:v1) do |xsl|
|
104
|
+
xsl.define :article do |t|
|
105
|
+
t.attribute :published_on, :type => :date
|
106
|
+
end
|
107
|
+
end
|
108
|
+
element = Collection.new [@article], :node => :article, :schema => @schema
|
109
|
+
doc = element.build_xml.doc
|
110
|
+
doc.xpath("/articles/article[@published_on=1956-03-05]").should be
|
111
|
+
end
|
112
|
+
|
113
|
+
it "emits nil when the attribute value is nil" do
|
114
|
+
@schema = Schema.version(:v1) do |xsl|
|
115
|
+
xsl.define :article do |t|
|
116
|
+
t.attribute :published_on, :type => :date
|
117
|
+
end
|
118
|
+
end
|
119
|
+
@article.published_on = nil
|
120
|
+
element = Collection.new [@article], :node => :article, :schema => @schema
|
121
|
+
doc = element.build_xml.doc
|
122
|
+
doc.xpath("/articles/article[@published_on='']").should be
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "with a has_many element" do
|
127
|
+
before do
|
128
|
+
@schema = Schema.version(:v1) do |xsl|
|
129
|
+
xsl.define :article do |t|
|
130
|
+
t.has_many :comments
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
@article = Article.new :title => Faker::Company.bs
|
135
|
+
@comment = Comment.new :article => @article, :text => Faker::Company.bs
|
136
|
+
@article.comments = [@comment]
|
137
|
+
end
|
138
|
+
|
139
|
+
it "emits each of the child objects" do
|
140
|
+
@schema.define :comment do |t|
|
141
|
+
t.string :text
|
142
|
+
end
|
143
|
+
|
144
|
+
element = Collection.new [@article],
|
145
|
+
:node => :article,
|
146
|
+
:schema => @schema
|
147
|
+
doc = element.build_xml.doc
|
148
|
+
doc.xpath("/articles/article/comments/comment/text").first.inner_text.should == @comment.text
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "with custom value procs" do
|
153
|
+
before do
|
154
|
+
@schema = Schema.version(:v1) do |xsl|
|
155
|
+
xsl.define :article do |t|
|
156
|
+
t.attribute :id, :value => proc {|attribute| "foo" }
|
157
|
+
t.string :id, :value => proc {|element| "foo" }
|
158
|
+
t.has_many :comments
|
159
|
+
end
|
160
|
+
|
161
|
+
xsl.define :comment do |t|
|
162
|
+
t.string :article_title, :value => proc{|element| element.object.article.title }
|
163
|
+
t.belongs_to :user
|
164
|
+
end
|
165
|
+
|
166
|
+
xsl.define :user do |t|
|
167
|
+
t.string :title, :value => proc{|element| element.parents[:article].title }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
@article1 = Article.new :title => Faker::Company.bs
|
172
|
+
@article2 = Article.new :title => Faker::Lorem.sentence
|
173
|
+
|
174
|
+
@user = User.new
|
175
|
+
@comment1 = Comment.new :article => @article1, :user => @user
|
176
|
+
@comment2 = Comment.new :article => @article2, :user => @user
|
177
|
+
|
178
|
+
@article1.comments = [@comment1]
|
179
|
+
@article2.comments = [@comment2]
|
180
|
+
end
|
181
|
+
|
182
|
+
it "emits the value of the value proc for attributes" do
|
183
|
+
element = Collection.new [@article1],
|
184
|
+
:node => :article,
|
185
|
+
:schema => @schema
|
186
|
+
doc = element.build_xml.doc
|
187
|
+
doc.xpath("/articles/article[@id=foo]").should be
|
188
|
+
end
|
189
|
+
|
190
|
+
it "emits the value of the value proc for elements" do
|
191
|
+
element = Collection.new [@article1],
|
192
|
+
:node => :article,
|
193
|
+
:schema => @schema
|
194
|
+
doc = element.build_xml.doc
|
195
|
+
doc.xpath("/articles/article/id").first.inner_text.should == "foo"
|
196
|
+
end
|
197
|
+
|
198
|
+
it "emits the value of the value proc, which is passed an element containing a reference to the object" do
|
199
|
+
element = Collection.new [@article1],
|
200
|
+
:node => :article,
|
201
|
+
:schema => @schema
|
202
|
+
doc = element.build_xml.doc
|
203
|
+
doc.xpath("/articles/article/comments/comment/article_title").first.inner_text.should == @article1.title
|
204
|
+
end
|
205
|
+
|
206
|
+
it "emits the value of the value proc, which is passed an element containing a reference all ancestor objects" do
|
207
|
+
element = Collection.new [@article1, @article2],
|
208
|
+
:node => :article,
|
209
|
+
:schema => @schema
|
210
|
+
doc = element.build_xml.doc
|
211
|
+
doc.xpath("/articles/article").length.should == 2
|
212
|
+
doc.xpath("/articles/article/comments/comment/user/title").first.inner_text.should == @article1.title
|
213
|
+
doc.xpath("/articles/article/comments/comment/user/title").last.inner_text.should == @article2.title
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe "with belongs_to elements marked as choice" do
|
218
|
+
before do
|
219
|
+
@schema = Schema.version(:v1) do |xsl|
|
220
|
+
xsl.define :article do |t|
|
221
|
+
t.string :title
|
222
|
+
end
|
223
|
+
|
224
|
+
xsl.define :user do |t|
|
225
|
+
t.string :username
|
226
|
+
end
|
227
|
+
|
228
|
+
xsl.define :comment do |t|
|
229
|
+
t.belongs_to :commentable, :choice => {
|
230
|
+
"Article" => :article,
|
231
|
+
"User" => :user
|
232
|
+
}
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
@article = Article.new :title => Faker::Company.bs
|
237
|
+
@user = User.new :username => Faker::Internet.user_name
|
238
|
+
@comment1 = Comment.new :commentable => @article
|
239
|
+
@comment2 = Comment.new :commentable => @user
|
240
|
+
@comment3 = Comment.new :commentable => nil
|
241
|
+
end
|
242
|
+
|
243
|
+
it "uses the name of the class to lookup the definition to be used" do
|
244
|
+
element = Collection.new [@comment1, @comment2, @comment3], :node => :comment, :schema => @schema
|
245
|
+
doc = element.build_xml.doc
|
246
|
+
doc.xpath("/comments/comment").length.should == 3
|
247
|
+
doc.xpath("/comments/comment/article/title").inner_text.should == @article.title
|
248
|
+
doc.xpath("/comments/comment/user/username").inner_text.should == @user.username
|
249
|
+
doc.xpath("/comments/comment")[2].inner_text.should be_empty
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
describe "custom builders" do
|
254
|
+
before do
|
255
|
+
@schema = Schema.version(:v1) do |xsl|
|
256
|
+
xsl.define :article, :builder_class => "ActiveApi::MyCustomClass"
|
257
|
+
end
|
258
|
+
|
259
|
+
class MyCustomClass < ActiveApi::ComplexType
|
260
|
+
def build(builder)
|
261
|
+
builder.send :foo, :bar => "baz" do |xml|
|
262
|
+
xml.send :woot, "lol"
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
@article = Article.new :id => 456
|
268
|
+
end
|
269
|
+
|
270
|
+
it "uses the custom builder class" do
|
271
|
+
element = Schema.find(:v1).build_xml [@article], :node => :article
|
272
|
+
doc = element.doc
|
273
|
+
doc.xpath("/articles/foo[@bar='baz']/woot").first.inner_text.should == "lol"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
describe "specifying a symbol as a value proc" do
|
278
|
+
before do
|
279
|
+
@schema = Schema.version(:v1) do |xsl|
|
280
|
+
xsl.define :article do |t|
|
281
|
+
t.string :foo, :value => :title
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
@article = Article.new :title => Faker::Company.bs
|
286
|
+
end
|
287
|
+
|
288
|
+
it "uses the custom builder class" do
|
289
|
+
element = Schema.find(:v1).build_xml [@article], :node => :article
|
290
|
+
doc = element.doc
|
291
|
+
doc.xpath("/articles/article/foo").first.inner_text.should == @article.title
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe "specifying a string literal as a value" do
|
296
|
+
before do
|
297
|
+
@schema = Schema.version(:v1) do |xsl|
|
298
|
+
xsl.define :article do |t|
|
299
|
+
t.string :foo, :value => "some value"
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
@article = Article.new :title => Faker::Company.bs
|
304
|
+
end
|
305
|
+
|
306
|
+
it "uses the custom builder class" do
|
307
|
+
element = Schema.find(:v1).build_xml [@article], :node => :article
|
308
|
+
doc = element.doc
|
309
|
+
doc.xpath("/articles/article/foo").first.inner_text.should == "some value"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
describe "specifying custom definition classes with a string" do
|
314
|
+
before do
|
315
|
+
class MyDefinitionClass < Definition
|
316
|
+
def timestamps
|
317
|
+
date_time :created_at
|
318
|
+
date_time :updated_at
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
@schema = Schema.version(:v1, :definition_class => "ActiveApi::MyDefinitionClass") do |xsl|
|
323
|
+
xsl.define :article do |t|
|
324
|
+
t.timestamps
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
@article = Article.new :created_at => Date.parse("12/21/1945"), :updated_at => Date.parse("4/5/1992")
|
329
|
+
end
|
330
|
+
|
331
|
+
it "uses the custom builder class" do
|
332
|
+
element = Schema.find(:v1).build_xml [@article], :node => :article
|
333
|
+
doc = element.doc
|
334
|
+
doc.xpath("/articles/article/created_at").first.inner_text.should be_starts_with("1945-12-21")
|
335
|
+
doc.xpath("/articles/article/updated_at").first.inner_text.should be_starts_with("1992-04-05")
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
describe "specifying custom definition classes with a class" do
|
340
|
+
before do
|
341
|
+
class MyDefinitionClass < Definition
|
342
|
+
def timestamps
|
343
|
+
date_time :created_at
|
344
|
+
date_time :updated_at
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
@schema = Schema.version(:v1, :definition_class => MyDefinitionClass) do |xsl|
|
349
|
+
xsl.define :article do |t|
|
350
|
+
t.timestamps
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
@article = Article.new :created_at => Date.parse("12/21/1945"), :updated_at => Date.parse("4/5/1992")
|
355
|
+
end
|
356
|
+
|
357
|
+
it "uses the custom builder class" do
|
358
|
+
element = Schema.find(:v1).build_xml [@article], :node => :article
|
359
|
+
doc = element.doc
|
360
|
+
doc.xpath("/articles/article/created_at").first.inner_text.should be_starts_with("1945-12-21")
|
361
|
+
doc.xpath("/articles/article/updated_at").first.inner_text.should be_starts_with("1992-04-05")
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|