add xml_name option to properties_dsl

also:
* only allow symbols as name and xml_name
* use public_send instead of send
This commit is contained in:
Benjamin Neff 2015-07-24 02:05:02 +02:00
parent 0deb74c103
commit 71b1d6dc1e
7 changed files with 84 additions and 29 deletions

View file

@ -8,10 +8,9 @@ module DiasporaFederation
property :guid property :guid
# @!attribute [r] diaspora_id # @!attribute [r] diaspora_id
# @todo refactoring with properties_dsl, xml name should be diaspora_handle
# The diaspora ID of the person # The diaspora ID of the person
# @return [String] diaspora ID # @return [String] diaspora ID
property :diaspora_id property :diaspora_id, xml_name: :diaspora_handle
# @!attribute [r] url # @!attribute [r] url
# @see WebFinger#seed_url # @see WebFinger#seed_url

View file

@ -3,11 +3,10 @@ module DiasporaFederation
# this entity contains all the profile data of a person # this entity contains all the profile data of a person
class Profile < Entity class Profile < Entity
# @!attribute [r] diaspora_id # @!attribute [r] diaspora_id
# @todo refactoring with properties_dsl, xml name should be diaspora_handle
# The diaspora ID of the person # The diaspora ID of the person
# @see Person#diaspora_id # @see Person#diaspora_id
# @return [String] diaspora ID # @return [String] diaspora ID
property :diaspora_id property :diaspora_id, xml_name: :diaspora_handle
# @!attribute [r] first_name # @!attribute [r] first_name
# @deprecated # @deprecated

View file

@ -15,6 +15,7 @@ module DiasporaFederation
# property :prop # property :prop
# property :optional, default: false # property :optional, default: false
# property :dynamic_default, default: -> { Time.now } # property :dynamic_default, default: -> { Time.now }
# property :another_prop, xml_name: :another_name
# entity :nested, NestedEntity # entity :nested, NestedEntity
# entity :multiple, [OtherEntity] # entity :multiple, [OtherEntity]
# end # end
@ -59,7 +60,7 @@ module DiasporaFederation
# @return [Hash] entity data (mostly equal to the hash used for initialization). # @return [Hash] entity data (mostly equal to the hash used for initialization).
def to_h def to_h
self.class.class_prop_names.each_with_object({}) do |prop, hash| self.class.class_prop_names.each_with_object({}) do |prop, hash|
hash[prop] = send(prop) hash[prop] = public_send(prop)
end end
end end
@ -120,24 +121,28 @@ module DiasporaFederation
doc = Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new) doc = Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new)
Nokogiri::XML::Element.new(self.class.entity_name, doc).tap do |root_element| Nokogiri::XML::Element.new(self.class.entity_name, doc).tap do |root_element|
self.class.class_props.each do |prop_def| self.class.class_props.each do |prop_def|
name = prop_def[:name] add_property_to_xml(doc, prop_def, root_element)
type = prop_def[:type] end
if type == String end
root_element << simple_node(doc, name) end
else
# call #to_xml for each item and append to root def add_property_to_xml(doc, prop_def, root_element)
[*send(name)].compact.each do |item| property = prop_def[:name]
root_element << item.to_xml type = prop_def[:type]
end if type == String
end root_element << simple_node(doc, prop_def[:xml_name], property)
else
# call #to_xml for each item and append to root
[*public_send(property)].compact.each do |item|
root_element << item.to_xml
end end
end end
end end
# create simple node, fill it with text and append to root # create simple node, fill it with text and append to root
def simple_node(doc, name) def simple_node(doc, name, property)
Nokogiri::XML::Element.new(name.to_s, doc).tap do |node| Nokogiri::XML::Element.new(name.to_s, doc).tap do |node|
data = send(name).to_s data = public_send(property).to_s
node.content = data unless data.empty? node.content = data unless data.empty?
end end
end end

View file

@ -6,6 +6,7 @@ module DiasporaFederation
# property :prop # property :prop
# property :optional, default: false # property :optional, default: false
# property :dynamic_default, default: -> { Time.now } # property :dynamic_default, default: -> { Time.now }
# property :another_prop, xml_name: :another_name
# entity :nested, NestedEntity # entity :nested, NestedEntity
# entity :multiple, [OtherEntity] # entity :multiple, [OtherEntity]
module PropertiesDSL module PropertiesDSL
@ -19,6 +20,7 @@ module DiasporaFederation
# @param [Hash] opts further options # @param [Hash] opts further options
# @option opts [Object, #call] :default a default value, making the # @option opts [Object, #call] :default a default value, making the
# property optional # property optional
# @option opts [Symbol] :xml_name another name used for xml generation
def property(name, opts={}) def property(name, opts={})
define_property name, String, opts define_property name, String, opts
end end
@ -69,7 +71,14 @@ module DiasporaFederation
def define_property(name, type, opts={}) def define_property(name, type, opts={})
raise InvalidName unless name_valid?(name) raise InvalidName unless name_valid?(name)
class_props << {name: name, type: type} xml_name = name
if opts.has_key? :xml_name
raise ArgumentError, "xml_name is not supported for nested entities" unless type == String
xml_name = opts[:xml_name]
raise InvalidName, "invalid xml_name" unless name_valid?(xml_name)
end
class_props << {name: name, xml_name: xml_name, type: type}
default_props[name] = opts[:default] if opts.has_key? :default default_props[name] = opts[:default] if opts.has_key? :default
instance_eval { attr_reader name } instance_eval { attr_reader name }
@ -79,7 +88,7 @@ module DiasporaFederation
# @param [String, Symbol] name the name to check # @param [String, Symbol] name the name to check
# @return [Boolean] # @return [Boolean]
def name_valid?(name) def name_valid?(name)
name.instance_of?(Symbol) || name.instance_of?(String) name.instance_of?(Symbol)
end end
# checks if the type extends {Entity} # checks if the type extends {Entity}

View file

@ -20,5 +20,10 @@ module DiasporaFederation
entity :test, TestEntity entity :test, TestEntity
entity :multi, [OtherEntity] entity :multi, [OtherEntity]
end end
class TestEntityWithXmlName < DiasporaFederation::Entity
property :test
property :qwer, xml_name: :asdf
end
end end
end end

View file

@ -61,7 +61,9 @@ module DiasporaFederation
it "contains nodes for each of the properties" do it "contains nodes for each of the properties" do
entity = Entities::TestDefaultEntity.new(data) entity = Entities::TestDefaultEntity.new(data)
entity.to_xml.children.each do |node| xml_children = entity.to_xml.children
expect(xml_children).to have_exactly(4).items
xml_children.each do |node|
expect(%w(test1 test2 test3 test4)).to include(node.name) expect(%w(test1 test2 test3 test4)).to include(node.name)
end end
end end
@ -92,6 +94,7 @@ module DiasporaFederation
it "gets xml-ified by #to_xml" do it "gets xml-ified by #to_xml" do
entity = Entities::TestNestedEntity.new(nested_data) entity = Entities::TestNestedEntity.new(nested_data)
xml = entity.to_xml xml = entity.to_xml
expect(xml.children).to have_exactly(4).items
xml.children.each do |node| xml.children.each do |node|
expect(%w(asdf test_entity other_entity)).to include(node.name) expect(%w(asdf test_entity other_entity)).to include(node.name)
end end
@ -99,5 +102,23 @@ module DiasporaFederation
expect(xml.xpath("other_entity")).to have_exactly(2).items expect(xml.xpath("other_entity")).to have_exactly(2).items
end end
end end
context "xml_name" do
let(:hash) { {test: "test", qwer: "qwer"} }
it "uses xml_name for the #to_xml" do
entity = Entities::TestEntityWithXmlName.new(hash)
xml_children = entity.to_xml.children
expect(xml_children).to have_exactly(2).items
xml_children.each do |node|
expect(%w(test asdf)).to include(node.name)
end
end
it "should not use the xml_name for the #to_h" do
entity = Entities::TestEntityWithXmlName.new(hash)
expect(entity.to_h).to eq(hash)
end
end
end end
end end

View file

@ -8,19 +8,12 @@ module DiasporaFederation
properties = dsl.class_props properties = dsl.class_props
expect(properties).to have(1).item expect(properties).to have(1).item
expect(properties.first[:name]).to eq(:test) expect(properties.first[:name]).to eq(:test)
expect(properties.first[:type]).to eq(String) expect(properties.first[:xml_name]).to eq(:test)
end
it "can name simple properties by string" do
dsl.property "test"
properties = dsl.class_props
expect(properties).to have(1).item
expect(properties.first[:name]).to eq("test")
expect(properties.first[:type]).to eq(String) expect(properties.first[:type]).to eq(String)
end end
it "will not accept other types for names" do it "will not accept other types for names" do
[1234, true, {}].each do |val| ["test", 1234, true, {}].each do |val|
expect { expect {
dsl.property val dsl.property val
}.to raise_error PropertiesDSL::InvalidName }.to raise_error PropertiesDSL::InvalidName
@ -34,8 +27,26 @@ module DiasporaFederation
properties = dsl.class_props properties = dsl.class_props
expect(properties).to have(3).items expect(properties).to have(3).items
expect(properties.map {|e| e[:name] }).to include(:test, :asdf, :zzzz) expect(properties.map {|e| e[:name] }).to include(:test, :asdf, :zzzz)
expect(properties.map {|e| e[:xml_name] }).to include(:test, :asdf, :zzzz)
properties.each {|e| expect(e[:type]).to eq(String) } properties.each {|e| expect(e[:type]).to eq(String) }
end end
it "can add an xml name to simple properties with a symbol" do
dsl.property :test, xml_name: :xml_test
properties = dsl.class_props
expect(properties).to have(1).item
expect(properties.first[:name]).to eq(:test)
expect(properties.first[:xml_name]).to eq(:xml_test)
expect(properties.first[:type]).to eq(String)
end
it "will not accept other types for xml names" do
["test", 1234, true, {}].each do |val|
expect {
dsl.property :test, xml_name: val
}.to raise_error PropertiesDSL::InvalidName, "invalid xml_name"
end
end
end end
context "nested entities" do context "nested entities" do
@ -75,6 +86,12 @@ module DiasporaFederation
}.to raise_error PropertiesDSL::InvalidType }.to raise_error PropertiesDSL::InvalidType
end end
end end
it "can not add an xml name to a nested entity" do
expect {
dsl.entity :other, Entities::TestEntity, xml_name: :other_name
}.to raise_error ArgumentError, "xml_name is not supported for nested entities"
end
end end
describe ".default_values" do describe ".default_values" do