gyoku-1.3.1/0000755000175000017500000000000012551205233012220 5ustar globusglobusgyoku-1.3.1/lib/0000755000175000017500000000000012551205233012766 5ustar globusglobusgyoku-1.3.1/lib/gyoku.rb0000644000175000017500000000051612551205233014453 0ustar globusglobusrequire "gyoku/version" require "gyoku/hash" module Gyoku # Converts a given Hash +key+ with +options+ into an XML tag. def self.xml_tag(key, options = {}) XMLKey.create(key, options) end # Translates a given +hash+ with +options+ to XML. def self.xml(hash, options = {}) Hash.to_xml hash.dup, options end end gyoku-1.3.1/lib/gyoku/0000755000175000017500000000000012551205233014124 5ustar globusglobusgyoku-1.3.1/lib/gyoku/hash.rb0000644000175000017500000000666712551205233015413 0ustar globusglobusrequire "builder" require "gyoku/array" require "gyoku/xml_key" require "gyoku/xml_value" module Gyoku class Hash # Translates a given +hash+ with +options+ to XML. def self.to_xml(hash, options = {}) iterate_with_xml hash do |xml, key, value, attributes| self_closing = key.to_s[-1, 1] == "/" escape_xml = key.to_s[-1, 1] != "!" xml_key = XMLKey.create key, options case when :content! === key then xml << XMLValue.create(value, escape_xml, options) when ::Array === value then xml << Array.to_xml(value, xml_key, escape_xml, attributes, options.merge(:self_closing => self_closing)) when ::Hash === value then xml.tag!(xml_key, attributes) { xml << Hash.to_xml(value, options) } when self_closing then xml.tag!(xml_key, attributes) when NilClass === value then xml.tag!(xml_key, "xsi:nil" => "true") else xml.tag!(xml_key, attributes) { xml << XMLValue.create(value, escape_xml, options) } end end end private # Iterates over a given +hash+ and yields a builder +xml+ instance, the current # Hash +key+ and any XML +attributes+. # # Keys beginning with "@" are treated as explicit attributes for their container. # You can use both :attributes! and "@" keys to specify attributes. # In the event of a conflict, the "@" key takes precedence. def self.iterate_with_xml(hash) xml = Builder::XmlMarkup.new attributes = hash[:attributes!] || {} hash_without_attributes = hash.reject { |key, value| key == :attributes! } order(hash_without_attributes).each do |key| node_attr = attributes[key] || {} # node_attr must be kind of ActiveSupport::HashWithIndifferentAccess node_attr = ::Hash[node_attr.map { |k,v| [k.to_s, v] }] node_value = hash[key].respond_to?(:keys) ? hash[key].clone : hash[key] if node_value.respond_to?(:keys) explicit_keys = node_value.keys.select{|k| k.to_s =~ /^@/ } explicit_attr = {} explicit_keys.each{|k| explicit_attr[k.to_s[1..-1]] = node_value[k]} node_attr.merge!(explicit_attr) explicit_keys.each{|k| node_value.delete(k) } tmp_node_value = node_value.delete(:content!) node_value = tmp_node_value unless tmp_node_value.nil? node_value = "" if node_value.respond_to?(:empty?) && node_value.empty? end yield xml, key, node_value, node_attr end xml.target! end # Deletes and returns an Array of keys stored under the :order! key of a given +hash+. # Defaults to return the actual keys of the Hash if no :order! key could be found. # Raises an ArgumentError in case the :order! Array does not match the Hash keys. def self.order(hash) order = hash[:order!] || hash.delete('order!') hash_without_order = hash.reject { |key, value| key == :order! } order = hash_without_order.keys unless order.kind_of? ::Array # Ignore Explicit Attributes orderable = order.delete_if{|k| k.to_s =~ /^@/ } hashable = hash_without_order.keys.select{|k| !(k.to_s =~ /^@/) } missing, spurious = hashable - orderable, orderable - hashable raise ArgumentError, "Missing elements in :order! #{missing.inspect}" unless missing.empty? raise ArgumentError, "Spurious elements in :order! #{spurious.inspect}" unless spurious.empty? order end end end gyoku-1.3.1/lib/gyoku/array.rb0000644000175000017500000000525412551205233015575 0ustar globusglobusrequire "builder" require "gyoku/hash" require "gyoku/xml_value" module Gyoku class Array NESTED_ELEMENT_NAME = "element" # Translates a given +array+ to XML. Accepts the XML +key+ to add the elements to, # whether to +escape_xml+ and an optional Hash of +attributes+. def self.to_xml(array, key, escape_xml = true, attributes = {}, options = {}) self_closing = options.delete(:self_closing) unwrap = options[:unwrap] || false iterate_with_xml array, key, attributes, options do |xml, item, attrs, index| if self_closing xml.tag!(key, attrs) else case item when ::Hash then if unwrap xml << Hash.to_xml(item, options) else xml.tag!(key, attrs) { xml << Hash.to_xml(item, options) } end when ::Array then xml.tag!(key, attrs) { xml << Array.to_xml(item, NESTED_ELEMENT_NAME) } when NilClass then xml.tag!(key, "xsi:nil" => "true") else xml.tag!(key, attrs) { xml << XMLValue.create(item, escape_xml) } end end end end private # Iterates over a given +array+ with a Hash of +attributes+ and yields a builder +xml+ # instance, the current +item+, any XML +attributes+ and the current +index+. def self.iterate_with_xml(array, key, attributes, options, &block) xml = Builder::XmlMarkup.new unwrap = options[:unwrap] || false if (unwrap) xml.tag!(key) { iterate_array(xml, array, attributes, &block) } else iterate_array(xml, array, attributes, &block) end xml.target! end # Iterates over a given +array+ with a Hash of +attributes+ and yields a builder +xml+ # instance, the current +item+, any XML +attributes+ and the current +index+. def self.iterate_array(xml, array, attributes, &block) array.each_with_index do |item, index| if item.respond_to?(:keys) attrs = item.reduce({}) do |st, v| k = v[0].to_s st[k[1..-1]] = v[1].to_s if k =~ /^@/ st end else attrs = {} end yield xml, item, tag_attributes(attributes, index).merge(attrs), index end end # Takes a Hash of +attributes+ and the +index+ for which to return attributes # for duplicate tags. def self.tag_attributes(attributes, index) return {} if attributes.empty? attributes.inject({}) do |hash, (key, value)| value = value[index] if value.kind_of? ::Array value ? hash.merge(key => value) : hash end end end end gyoku-1.3.1/lib/gyoku/xml_key.rb0000644000175000017500000000435712551205233016132 0ustar globusglobusmodule Gyoku module XMLKey class << self CAMELCASE = lambda { |key| key.gsub(/\/(.?)/) { |m| "::#{m.split('').last.upcase}" }.gsub(/(?:^|_)(.)/) { |m| m.split('').last.upcase } } LOWER_CAMELCASE = lambda { |key| key[0].chr.downcase + CAMELCASE.call(key)[1..-1] } UPCASE = lambda { |key| key.upcase } FORMULAS = { :lower_camelcase => lambda { |key| LOWER_CAMELCASE.call(key) }, :camelcase => lambda { |key| CAMELCASE.call(key) }, :upcase => lambda { |key| UPCASE.call(key) }, :none => lambda { |key| key } } # Converts a given +object+ with +options+ to an XML key. def create(key, options = {}) xml_key = chop_special_characters key.to_s if unqualified = unqualify?(xml_key) xml_key = xml_key.split(":").last end xml_key = key_converter(options, xml_key).call(xml_key) if Symbol === key if !unqualified && qualify?(options) && !xml_key.include?(":") xml_key = "#{options[:namespace]}:#{xml_key}" end xml_key end private # Returns the formula for converting Symbol keys. def key_converter(options, xml_key) return options[:key_converter] if options[:key_converter].is_a? Proc defined_key = options[:key_to_convert] if (defined_key != nil) && (defined_key == xml_key) key_converter = options[:key_converter] elsif defined_key != nil key_converter = :lower_camelcase elsif (options[:except] == xml_key) key_converter = :lower_camelcase else key_converter = options[:key_converter] || :lower_camelcase end FORMULAS[key_converter] end # Chops special characters from the end of a given +string+. def chop_special_characters(string) ["!", "/"].include?(string[-1, 1]) ? string.chop : string end # Returns whether to remove the namespace from a given +key+. def unqualify?(key) key[0, 1] == ":" end # Returns whether to namespace all keys (elementFormDefault). def qualify?(options) options[:element_form_default] == :qualified && options[:namespace] end end end end gyoku-1.3.1/lib/gyoku/xml_value.rb0000644000175000017500000000174112551205233016450 0ustar globusglobusrequire "cgi" require "date" module Gyoku module XMLValue class << self # xs:date format XS_DATE_FORMAT = "%Y-%m-%d" # xs:time format XS_TIME_FORMAT = "%H:%M:%S" # xs:dateTime format XS_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S%Z" # Converts a given +object+ to an XML value. def create(object, escape_xml = true, options = {}) if Time === object object.strftime XS_TIME_FORMAT elsif DateTime === object object.strftime XS_DATETIME_FORMAT elsif Date === object object.strftime XS_DATE_FORMAT elsif String === object escape_xml ? CGI.escapeHTML(object) : object elsif object.respond_to?(:to_datetime) create object.to_datetime elsif object.respond_to?(:call) create object.call elsif ::Hash === object Gyoku::Hash.to_xml(object, options) else object.to_s end end end end end gyoku-1.3.1/lib/gyoku/version.rb0000644000175000017500000000004512551205233016135 0ustar globusglobusmodule Gyoku VERSION = '1.3.1' end gyoku-1.3.1/.gitignore0000644000175000017500000000011612551205233014206 0ustar globusglobus.DS_Store .yardoc doc coverage tmp *~ *.swp *.gem .bundle Gemfile.lock .rvmrc gyoku-1.3.1/Rakefile0000644000175000017500000000032512551205233013665 0ustar globusglobusrequire "bundler" require "bundler/setup" Bundler::GemHelper.install_tasks require "rspec/core/rake_task" RSpec::Core::RakeTask.new do |t| t.rspec_opts = %w(-c) end task :default => :spec task :test => :spec gyoku-1.3.1/MIT-LICENSE0000644000175000017500000000204512551205233013655 0ustar globusglobusCopyright (c) 2010 Daniel Harrington Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gyoku-1.3.1/spec/0000755000175000017500000000000012551205233013152 5ustar globusglobusgyoku-1.3.1/spec/gyoku/0000755000175000017500000000000012551205233014310 5ustar globusglobusgyoku-1.3.1/spec/gyoku/xml_value_spec.rb0000644000175000017500000000410612551205233017644 0ustar globusglobusrequire "spec_helper" describe Gyoku::XMLValue do describe ".create" do context "for DateTime objects" do it "returns an xs:dateTime compliant String" do expect(create(DateTime.new(2012, 03, 22, 16, 22, 33))).to eq("2012-03-22T16:22:33+00:00") end end context "for Date objects" do it "returns an xs:date compliant String" do expect(create(Date.new(2012, 03, 22))).to eq("2012-03-22") end end context "for Time objects" do it "returns an xs:time compliant String" do expect(create(Time.local(2012, 03, 22, 16, 22, 33))).to eq("16:22:33") end end it "returns the String value and escapes special characters" do expect(create("string")).to eq("string") expect(create("")).to eq("<tag>") expect(create("at&t")).to eq("at&t") expect(create('"quotes"')).to eq(""quotes"") end it "returns the String value without escaping special characters" do expect(create("", false)).to eq("") end it "returns an xs:dateTime compliant String for Objects responding to #to_datetime" do singleton = Object.new def singleton.to_datetime DateTime.new 2012, 03, 22, 16, 22, 33 end expect(create(singleton)).to eq("2012-03-22T16:22:33+00:00") end it "calls Proc objects and converts their return value" do object = lambda { DateTime.new 2012, 03, 22, 16, 22, 33 } expect(create(object)).to eq("2012-03-22T16:22:33+00:00") end it "hash objects get converted to xml" do object = { document!: { "@version" => "2.0", content!: { key!: "value", other_key: { "@attribute" => 'value', content!: { key: "value" } } } } } expect(create(object)).to eq("valuevalue") end it "calls #to_s unless the Object responds to #to_datetime" do expect(create("value")).to eq("value") end end def create(object, escape_xml = true) Gyoku::XMLValue.create object, escape_xml end end gyoku-1.3.1/spec/gyoku/array_spec.rb0000644000175000017500000000446712551205233017000 0ustar globusglobusrequire "spec_helper" describe Gyoku::Array do describe ".to_xml" do it "returns the XML for an Array of Hashes" do array = [{ :name => "adam" }, { :name => "eve" }] result = "adameve" expect(to_xml(array, "user")).to eq(result) end it "returns the XML for an Array of Hashes unwrapped" do array = [{ :name => "adam" }, { :name => "eve" }] result = "adameve" expect(to_xml(array, "user", true, {}, :unwrap => true)).to eq(result) end it "returns the XML for an Array of different Objects" do array = [:symbol, "string", 123] result = "symbolstring123" expect(to_xml(array, "value")).to eq(result) end it "defaults to escape special characters" do array = ["", "adam & eve"] result = "<tag />adam & eve" expect(to_xml(array, "value")).to eq(result) end it "does not escape special characters when told to" do array = ["", "adam & eve"] result = "adam & eve" expect(to_xml(array, "value", false)).to eq(result) end it "adds attributes to a given tag" do array = ["adam", "eve"] result = 'adameve' expect(to_xml(array, "value", :escape_xml, :active => true)).to eq(result) end it "adds attributes to duplicate tags" do array = ["adam", "eve"] result = 'adameve' expect(to_xml(array, "value", :escape_xml, :id => [1, 2])).to eq(result) end it "skips attribute for element without attributes if there are fewer attributes than elements" do array = ["adam", "eve", "serpent"] result = 'adameveserpent' expect(to_xml(array, "value", :escape_xml, :id => [1, 2])).to eq(result) end it "handles nested Arrays" do array = [["one", "two"]] result = "onetwo" expect(to_xml(array, "value")).to eq(result) end end def to_xml(*args) Gyoku::Array.to_xml *args end end gyoku-1.3.1/spec/gyoku/xml_key_spec.rb0000644000175000017500000000545312551205233017326 0ustar globusglobusrequire "spec_helper" describe Gyoku::XMLKey do describe ".create" do it "removes exclamation marks from the end of a String" do expect(create("value!")).to eq("value") end it "removes forward slashes from the end of a String" do expect(create("self-closing/")).to eq("self-closing") end it "does not convert snake_case Strings" do expect(create("lower_camel_case")).to eq("lower_camel_case") end it "converts snake_case Symbols to lowerCamelCase Strings" do expect(create(:lower_camel_case)).to eq("lowerCamelCase") expect(create(:lower_camel_case!)).to eq("lowerCamelCase") end context "when the converter option is set to camelcase" do it "should replace / with ::, and turn snake case into camel case" do input = "hello_world_bob/how_are_you|there:foo^bar".to_sym expected_output = "HelloWorldBob::HowAreYou|there:foo^bar" expect(create(input, {key_converter: :camelcase})).to eq(expected_output) end end context "with key_converter" do it "accepts lambda converters" do expect(create(:some_text, {key_converter: lambda { |k| k.reverse }})).to eq("txet_emos") end it "convert symbol to the specified type" do expect(create(:some_text, {key_converter: :camelcase})).to eq("SomeText") expect(create(:some_text, {key_converter: :upcase})).to eq("SOME_TEXT") expect(create(:some_text, {key_converter: :none})).to eq("some_text") end it "when key_to_convert is defined, convert only this key" do options = {key_converter: :camelcase, key_to_convert: 'somekey'} expect(create(:some_key, options)).to eq("someKey") options = {key_converter: :camelcase, key_to_convert: 'some_key'} expect(create(:some_key, options)).to eq("SomeKey") end it "when except is defined, dont convert this key" do options = {key_converter: :camelcase, except: 'some_key'} expect(create(:some_key, options)).to eq("someKey") end end context "with :element_form_default set to :qualified and a :namespace" do it "adds the given namespace" do key = create :qualify, :element_form_default => :qualified, :namespace => :v1 expect(key).to eq("v1:qualify") end it "does not add the given namespace if the key starts with a colon" do key = create ":qualify", :element_form_default => :qualified, :namespace => :v1 expect(key).to eq("qualify") end it "adds a given :namespace after converting the key" do key = create :username, :element_form_default => :qualified, :namespace => :v1, :key_converter => :camelcase expect(key).to eq("v1:Username") end end end def create(key, options = {}) Gyoku::XMLKey.create(key, options) end end gyoku-1.3.1/spec/gyoku/hash_spec.rb0000644000175000017500000003373712551205233016607 0ustar globusglobusrequire "spec_helper" describe Gyoku::Hash do describe ".to_xml" do describe "returns SOAP request compatible XML" do it "for a simple Hash" do expect(to_xml(:some => "user")).to eq("user") end it "for a nested Hash" do expect(to_xml(:some => { :new => "user" })).to eq("user") end context "with key_converter" do it "expect all keys change" do expect(to_xml({:some => { :new => "user" }}, {key_converter: :camelcase})).to eq("user") end it "and key_to_convert option should change only key" do hash = {:some => { :new => "user", :age => 20 }} options = {key_converter: :camelcase, key_to_convert: "some"} result = "user20" expect(to_xml(hash, options)).to eq(result) hash = {:some => { :new => "user", :age => 20 }} options = {key_converter: :camelcase, key_to_convert: "new"} result = "user20" expect(to_xml(hash, options)).to eq(result) end it "with except option, dont convert this key" do hash = {:some => { :new => "user", :age => 20 }} options = {key_converter: :camelcase, except: "some"} result = "user20" expect(to_xml(hash, options)).to eq(result) end end it "for a Hash with multiple keys" do expect(to_xml(:all => "users", :before => "whatever")).to include( "users", "whatever" ) end it "for a Hash containing an Array" do expect(to_xml(:some => ["user", "gorilla"])).to eq("usergorilla") end it "for a Hash containing an Array of Hashes" do expect(to_xml(:some => [{ :new => "user" }, { :old => "gorilla" }])). to eq("usergorilla") end end it "converts Hash key Symbols to lowerCamelCase" do expect(to_xml(:find_or_create => "user")).to eq("user") end it "does not convert Hash key Strings" do expect(to_xml("find_or_create" => "user")).to eq("user") end it "converts DateTime objects to xs:dateTime compliant Strings" do expect(to_xml(:before => DateTime.new(2012, 03, 22, 16, 22, 33))). to eq("2012-03-22T16:22:33+00:00") end it "converts Objects responding to to_datetime to xs:dateTime compliant Strings" do singleton = Object.new def singleton.to_datetime DateTime.new(2012, 03, 22, 16, 22, 33) end expect(to_xml(:before => singleton)).to eq("2012-03-22T16:22:33+00:00") end it "calls to_s on Strings even if they respond to to_datetime" do singleton = "gorilla" def singleton.to_datetime DateTime.new(2012, 03, 22, 16, 22, 33) end expect(to_xml(:name => singleton)).to eq("gorilla") end it "properly serializes nil values" do expect(to_xml(:some => nil)).to eq('') end it "creates self-closing tags for Hash keys ending with a forward slash" do expect(to_xml("self-closing/" => nil)).to eq('') end it "calls to_s on any other Object" do [666, true, false].each do |object| expect(to_xml(:some => object)).to eq("#{object}") end end it "defaults to escape special characters" do result = to_xml(:some => { :nested => "" }, :tag => "") expect(result).to include("<tag />") expect(result).to include("<tag />") end it "does not escape special characters for keys marked with an exclamation mark" do result = to_xml(:some => { :nested! => "" }, :tag! => "") expect(result).to include("") expect(result).to include("") end it "preserves the order of Hash keys and values specified through :order!" do hash = { :find_user => { :name => "Lucy", :id => 666, :order! => [:id, :name] } } result = "666Lucy" expect(to_xml(hash)).to eq(result) hash = { :find_user => { :mname => "in the", :lname => "Sky", :fname => "Lucy", :order! => [:fname, :mname, :lname] } } result = "Lucyin theSky" expect(to_xml(hash)).to eq(result) end it "preserves the order of Hash keys and values specified through 'order!' (as a string key)" do hash = { :find_user => { :name => "Lucy", :id => 666, 'order!' => [:id, :name] } } result = "666Lucy" expect(to_xml(hash)).to eq(result) hash = { :find_user => { :mname => "in the", :lname => "Sky", :fname => "Lucy", 'order!' => [:fname, :mname, :lname] } } result = "Lucyin theSky" expect(to_xml(hash)).to eq(result) end it "uses :order! symbol values for ordering but leaves the string key 'order!' if both are present" do hash = { :find_user => { :name => "Lucy", :id => 666, 'order!' => 'value', :order! => [:id, :name, 'order!'] } } result = "666Lucyvalue" expect(to_xml(hash)).to eq(result) end it "raises if the :order! Array is missing Hash keys" do hash = { :name => "Lucy", :id => 666, :order! => [:name] } expect { to_xml(hash) }.to raise_error(ArgumentError, "Missing elements in :order! [:id]") end it "raises if the :order! Array contains missing Hash keys" do hash = { :by_name => { :first_name => "Lucy", :last_name => "Sky", :order! => [:first_name, :middle_name, :last_name] } } expect { to_xml(hash) }.to raise_error(ArgumentError, "Spurious elements in :order! [:middle_name]") end it "adds attributes to Hash keys specified through :attributes!" do hash = { :find_user => { :person => "Lucy", :attributes! => { :person => { :id => 666 } } } } result = 'Lucy' expect(to_xml(hash)).to eq(result) hash = { :find_user => { :person => "Lucy", :attributes! => { :person => { :id => 666, :city => "Hamburg" } } } } expect(to_xml(hash)).to include('id="666"', 'city="Hamburg"') end it "adds attributes to duplicate Hash keys specified through :attributes!" do hash = { :find_user => { :person => ["Lucy", "Anna"], :attributes! => { :person => { :id => [1, 3] } } } } result = 'LucyAnna' expect(to_xml(hash)).to eq(result) hash = { :find_user => { :person => ["Lucy", "Anna"], :attributes! => { :person => { :active => "true" } } } } result = 'LucyAnna' expect(to_xml(hash)).to eq(result) end it "skips attribute for element without attributes if there are fewer attributes than elements" do hash = { :find_user => { :person => ["Lucy", "Anna", "Beth"], :attributes! => { :person => { :id => [1, 3] } } } } result = 'LucyAnnaBeth' expect(to_xml(hash)).to eq(result) end it "adds attributes to self-closing tags" do hash = { "category/" => "", :attributes! => { "category/" => { :id => 1 } } } expect(to_xml(hash)).to eq('') end it "recognizes @attribute => value along :attributes!" do hash = { "category" => { :content! => "users", :@id => 1 } } expect(to_xml(hash)).to eq('users') end it "recognizes @attribute => value along :attributes! in selfclosed tags" do hash = { "category/" => { :@id => 1 } } expect(to_xml(hash)).to eq('') end it ":@attribute => value takes over :attributes!" do hash = { "category/" => { :@id => 1 }, :attributes! => { "category/" => { 'id' => 2, # will be ignored 'type' => 'admins' } } } # attribute order is undefined expect(['','']).to include to_xml(hash) # with symbols hash = { "category/" => { :@id => 1 }, :attributes! => { "category/" => { :id => 2, # will be ignored :type => 'admins' } } } expect(['','']).to include to_xml(hash) end it "recognizes :content! => value as tag content" do hash = { "category" => { :content! => "users" } } expect(to_xml(hash)).to eq("users") end it "recognizes :content! => value as tag content with value Fixnum" do hash = { "category" => { :content! => 666 } } expect(to_xml(hash)).to eq("666") end it "recognizes :content! => value as tag content with value true" do hash = { "category" => { :content! => true } } expect(to_xml(hash)).to eq("true") end it "recognizes :content! => value as tag content with value false" do hash = { "category" => { :content! => false } } expect(to_xml(hash)).to eq("false") end it "recognizes :content! => value as tag content with value DateTime" do hash = { "before" => { :content! => DateTime.new(2012, 03, 22, 16, 22, 33) } } expect(to_xml(hash)).to eq("2012-03-22T16:22:33+00:00") end it "ignores :content! if self-closing mark present" do hash = { "category/" => { :content! => "users" } } expect(to_xml(hash)).to eq("") end it "recognizes array of attributes" do hash = { "category" => [{:@name => 'one'}, {:@name => 'two'}] } expect(to_xml(hash)).to eq('') # issue #31. hash = { :order! => ['foo', 'bar'], 'foo' => { :@foo => 'foo' }, 'bar' => { :@bar => 'bar', 'baz' => { } }, } expect(to_xml(hash)).to eq('') end it "recognizes array of attributes with content in each" do hash = { "foo" => [{:@name => "bar", :content! => 'gyoku'}, {:@name => "baz", :@some => "attr", :content! => 'rocks!'}] } expect([ 'gyokurocks!', 'gyokurocks!' ]).to include to_xml(hash) end it "recognizes array of attributes but ignores content in each if selfclosing" do hash = { "foo/" => [{:@name => "bar", :content! => 'gyoku'}, {:@name => "baz", :@some => "attr", :content! => 'rocks!'}] } expect([ '', '' ]).to include to_xml(hash) end it "recognizes array of attributes with selfclosing tag" do hash = { "category/" => [{:@name => 'one'}, {:@name => 'two'}] } expect(to_xml(hash)).to eq('') end context "with :element_form_default set to :qualified and a :namespace" do it "adds the given :namespace to every element" do hash = { :first => { "first_name" => "Lucy" }, ":second" => { :":first_name" => "Anna" }, "v2:third" => { "v2:firstName" => "Danie" } } result = to_xml hash, :element_form_default => :qualified, :namespace => :v1 expect(result).to include( "Lucy", "Anna", "Danie" ) end it "adds given :namespace to every element in an array" do hash = { :array => [ :first => "Lucy", :second => "Anna" ]} result = to_xml hash, :element_form_default => :qualified, :namespace => :v1 expect(result).to include("", "Lucy", "Anna") end end it "does not remove special keys from the original Hash" do hash = { :persons => { :first => "Lucy", :second => "Anna", :order! => [:second, :first], :attributes! => { :first => { :first => true } } }, :countries => [:de, :us], :order! => [:countries, :persons], :attributes! => { :countries => { :array => true } } } to_xml(hash) expect(hash).to eq({ :persons => { :first => "Lucy", :second => "Anna", :order! => [:second, :first], :attributes! => { :first => { :first => true } } }, :countries => [:de, :us], :order! => [:countries, :persons], :attributes! => { :countries => { :array => true } } }) end end it "doesn't modify original hash parameter by deleting its attribute keys" do hash = { :person => {:name => "Johnny", :surname => "Bravo", :"@xsi:type" => "People"} } to_xml(hash) expect(hash).to eq({:person=>{:name=>"Johnny", :surname=>"Bravo", :"@xsi:type"=>"People"}}) end def to_xml(hash, options = {}) Gyoku::Hash.to_xml hash, options end end gyoku-1.3.1/spec/gyoku_spec.rb0000644000175000017500000000474212551205233015656 0ustar globusglobusrequire "spec_helper" describe Gyoku do describe ".xml_tag" do it "translates Symbols to lowerCamelCase by default" do tag = Gyoku.xml_tag(:user_name) expect(tag).to eq("userName") end it "does not translate Strings" do tag = Gyoku.xml_tag("user_name") expect(tag).to eq("user_name") end it "translates Symbols by a given key_converter" do tag = Gyoku.xml_tag(:user_name, :key_converter => :upcase) expect(tag).to eq("USER_NAME") end it "does not translates Strings with a given key_converter" do tag = Gyoku.xml_tag("user_name", :key_converter => :upcase) expect(tag).to eq("user_name") end end describe ".xml" do it "translates a given Hash to XML" do hash = { :id => 1 } xml = Gyoku.xml(hash, :element_form_default => :qualified) expect(xml).to eq("1") end it "accepts a key_converter for the Hash keys" do hash = { :user_name => "finn", :pass_word => "secret" } xml = Gyoku.xml(hash, {key_converter: :upcase}) expect(xml).to include("finn") expect(xml).to include("secret") end it "don't converts Strings keys" do hash = { :user_name => "finn", "pass_word" => "secret" } xml = Gyoku.xml(hash, {key_converter: :upcase}) expect(xml).to include("finn") expect(xml).to include("secret") end it "when defined key_to_convert only convert this key" do hash = { user_name: "finn", pass_word: "secret" } options = {key_converter: :upcase, key_to_convert: 'user_name'} xml = Gyoku.xml(hash, options) expect(xml).to include("finn") expect(xml).to include("secret") end it "accepts key_converter for nested hash" do hash = { user: { user_name: "finn", pass_word: "secret" }} xml = Gyoku.xml(hash, {key_converter: :upcase}) expect(xml).to include("finn") expect(xml).to include("secret") end it "does not modify the original Hash" do hash = { :person => { :first_name => "Lucy", :last_name => "Sky", :order! => [:first_name, :last_name] }, :attributes! => { :person => { :id => "666" } } } original_hash = hash.dup Gyoku.xml(hash) expect(original_hash).to eq(hash) end end end gyoku-1.3.1/spec/spec_helper.rb0000644000175000017500000000042312551205233015767 0ustar globusglobusrequire 'bundler' Bundler.setup(:default, :development) unless RUBY_PLATFORM =~ /java/ require 'simplecov' require 'coveralls' SimpleCov.formatter = Coveralls::SimpleCov::Formatter SimpleCov.start do add_filter 'spec' end end require 'gyoku' require 'rspec' gyoku-1.3.1/CHANGELOG.md0000644000175000017500000001010012551205233014021 0ustar globusglobus## 1.3.1 (2015-04-05) * Feature: [#53](https://github.com/savonrb/gyoku/pull/53) Improved serialization of hashes nested in arrays. Thanks to @riburkes for this! ## 1.3.0 (2015-03-30) * Formally drop support for ruby 1.8.7 ## 1.2.3 (2015-03-10) # Feature: [#52](https://github.com/savonrb/gyoku/pull/52) Adds an :unwrap option that allows an array of hashes to be unwrapped into a single array xml node, rather than one per hash. ## 1.2.2 (2014-09-22) * Fixed a bug introduced by making Gyoku threadsafe. Who knew that `$1` and the block variable that `#gsub` provides are not the same? ## 1.2.1 (2014-09-22) * Fix : [#46](https://github.com/savonrb/gyoku/pull/46) Fixed an issue where Gyoku was not threadsafe. Gyoku should now be relatively more threadsafe due to less usage of global variables. ## 1.2.0 (2014-09-18) * Feature: [#44](https://github.com/savonrb/gyoku/pull/44) support for sorting via :order! with a string key ## 1.1.1 (2014-01-02) * Feature: [#38](https://github.com/savonrb/gyoku/pull/38) support for building nested Arrays * Feature: [#36](https://github.com/savonrb/gyoku/pull/36) allow setting any objects content with :content! * Deprecation: Support for ree and ruby 1.8.7 will be going away soon. ## 1.1.0 (2013-07-26) * Feature: [#30](https://github.com/savonrb/gyoku/pull/30) support for building Arrays of parent tags using @attributes. * Fix: [#21](https://github.com/savonrb/gyoku/pull/21) stop modifying the original Hash. The original issue is [savonrb/savon#410](https://github.com/savonrb/savon/issues/410). ## 1.0.0 (2012-12-17) * Refactoring: Removed the global configuration. This should really only affect the `Gyoku.convert_symbols_to` shortcut which was removed as well. If you're using Gyoku with Savon 2.0, there's now an option for that. If you're using Gyoku on itself, you can pass it the `:key_converter` option instead. ## 0.5.0 (2012-12-15) Feature: [#19](https://github.com/savonrb/gyoku/pull/19) adds support for explicit XML attributes. Feature: [#17](https://github.com/savonrb/gyoku/pull/17) adds an `:upcase` formula. ## 0.4.6 (2012-06-28) * Fix: [#16](https://github.com/rubiii/gyoku/issues/16) Date objects were mapped like DateTime objects. Gyoku.xml(date: Date.today) # => "2012-06-28" * Fix: Time objects were also mapped like DateTime objects. Gyoku.xml(time: sunday) # => "" ## 0.4.5 (2012-05-28) * Fix: [issue 8](https://github.com/rubiii/gyoku/issues/8) - Conflict between camelcase methods in Rails. * Fix: [pull request 15](https://github.com/rubiii/gyoku/pull/15) - Gyoku generates blank attribute values if there are fewer attribute values in attributes! than elements. * Fix: [issue 12](https://github.com/rubiii/gyoku/issues/12) - Don't remove special keys from the original Hash. ## 0.4.4 * Fix: [issue 6](https://github.com/rubiii/gyoku/issues/6) - `Gyoku.xml` does not modify the original Hash. ## 0.4.3 * Fix: Make sure `require "date"` when necessary. ## 0.4.2 * Fix: `Array.to_xml` so that the given :namespace is applied to every element in an Array. ## 0.4.1 * Fix: Alternative formulas and namespaces. ## 0.4.0 * Feature: Added alternative Symbol conversion formulas. You can choose between :lower_camelcase (the default), :camelcase and :none. Gyoku.convert_symbols_to :camelcase You can even define your own formula: Gyoku.convert_symbols_to { |key| key.upcase } ## 0.3.1 * Feature: Gyoku now calls Proc objects and converts their return value. ## 0.3.0 * Feature: Now when all Hash keys need to be namespaced (like with elementFormDefault), you can use options to to trigger this behavior. Gyoku.xml hash, :element_form_default => :qualified, :namespace => :v2 ## 0.2.0 * Feature: Added support for self-closing tags. Hash keys ending with a forward slash (regardless of their value) are now converted to self-closing tags. ## 0.1.1 * Fix: Allow people to use new versions of builder. ## 0.1.0 * Initial version. Gyoku was born as a core extension inside the [Savon](http://rubygems.org/gems/savon) library. gyoku-1.3.1/gyoku.gemspec0000644000175000017500000000156112551205233014726 0ustar globusglobus$:.push File.expand_path("../lib", __FILE__) require "gyoku/version" Gem::Specification.new do |s| s.name = "gyoku" s.version = Gyoku::VERSION s.platform = Gem::Platform::RUBY s.authors = "Daniel Harrington" s.email = "me@rubiii.com" s.homepage = "https://github.com/savonrb/#{s.name}" s.summary = "Translates Ruby Hashes to XML" s.description = "Gyoku translates Ruby Hashes to XML" s.required_ruby_version = '>= 1.9.2' s.rubyforge_project = "gyoku" s.license = "MIT" s.add_dependency "builder", ">= 2.1.2" s.add_development_dependency "rake" s.add_development_dependency "rspec" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] end gyoku-1.3.1/README.md0000644000175000017500000001177712551205233013514 0ustar globusglobus# Gyoku Gyoku translates Ruby Hashes to XML. ``` ruby Gyoku.xml(:find_user => { :id => 123, "v1:Key" => "api" }) # => "123api" ``` [![Build Status](https://secure.travis-ci.org/savonrb/gyoku.png?branch=master)](http://travis-ci.org/savonrb/gyoku) [![Gem Version](https://badge.fury.io/rb/gyoku.png)](http://badge.fury.io/rb/gyoku) [![Code Climate](https://codeclimate.com/github/savonrb/gyoku.png)](https://codeclimate.com/github/savonrb/gyoku) [![Coverage Status](https://coveralls.io/repos/savonrb/gyoku/badge.png?branch=master)](https://coveralls.io/r/savonrb/gyoku) ## Installation Gyoku is available through [Rubygems](http://rubygems.org/gems/gyoku) and can be installed via: ``` bash $ gem install gyoku ``` or add it to your Gemfile like this: ``` ruby gem 'gyoku', '~> 1.0' ``` ## Hash keys Hash key Symbols are converted to lowerCamelCase Strings. ``` ruby Gyoku.xml(:lower_camel_case => "key") # => "key" ``` You can change the default conversion formula to `:camelcase`, `:upcase` or `:none`. Note that options are passed as a second Hash to the `.xml` method. ``` ruby Gyoku.xml({ :camel_case => "key" }, { :key_converter => :camelcase }) # => "key" ``` Hash key Strings are not converted and may contain namespaces. ``` ruby Gyoku.xml("XML" => "key") # => "key" ``` ## Hash values * DateTime objects are converted to xs:dateTime Strings * Objects responding to :to_datetime (except Strings) are converted to xs:dateTime Strings * TrueClass and FalseClass objects are converted to "true" and "false" Strings * NilClass objects are converted to xsi:nil tags * These conventions are also applied to the return value of objects responding to :call * All other objects are converted to Strings using :to_s ## Special characters Gyoku escapes special characters unless the Hash key ends with an exclamation mark. ``` ruby Gyoku.xml(:escaped => "", :not_escaped! => "") # => "<tag />" ``` ## Self-closing tags Hash Keys ending with a forward slash create self-closing tags. ``` ruby Gyoku.xml(:"self_closing/" => "", "selfClosing/" => nil) # => "" ``` ## Sort XML tags In case you need the XML tags to be in a specific order, you can specify the order through an additional Array stored under the `:order!` key. ``` ruby Gyoku.xml(:name => "Eve", :id => 1, :order! => [:id, :name]) # => "1Eve" ``` ## XML attributes Adding XML attributes is rather ugly, but it can be done by specifying an additional Hash stored under the`:attributes!` key. ``` ruby Gyoku.xml(:person => "Eve", :attributes! => { :person => { :id => 1 } }) # => "Eve" ``` ## Explicit XML Attributes In addition to using the `:attributes!` key, you may also specify attributes through keys beginning with an "@" sign. Since you'll need to set the attribute within the hash containing the node's contents, a `:content!` key can be used to explicity set the content of the node. The `:content!` value may be a String, Hash, or Array. This is particularly useful for self-closing tags. **Using :attributes!** ``` ruby Gyoku.xml( "foo/" => "", :attributes! => { "foo/" => { "bar" => "1", "biz" => "2", "baz" => "3" } } ) # => "" ``` **Using "@" keys and ":content!"** ``` ruby Gyoku.xml( "foo/" => { :@bar => "1", :@biz => "2", :@baz => "3", :content! => "" }) # => "" ``` **Example using "@" to get Array of parent tags each with @attributes & :content!** ``` ruby Gyoku.xml( "foo" => [ {:@name => "bar", :content! => 'gyoku'} {:@name => "baz", :@some => "attr", :content! => 'rocks!'} ]) # => "gyokurocks!" ``` Naturally, it would ignore :content! if tag is self-closing: ``` ruby Gyoku.xml( "foo/" => [ {:@name => "bar", :content! => 'gyoku'} {:@name => "baz", :@some => "attr", :content! => 'rocks!'} ]) # => "" ``` This seems a bit more explicit with the attributes rather than having to maintain a hash of attributes. For backward compatibility, `:attributes!` will still work. However, "@" keys will override `:attributes!` keys if there is a conflict. ``` ruby Gyoku.xml(:person => {:content! => "Adam", :@id! => 0}) # => "Adam" ``` **Example with ":content!", :attributes! and "@" keys** ``` ruby Gyoku.xml({ :subtitle => { :@lang => "en", :content! => "It's Godzilla!" }, :attributes! => { :subtitle => { "lang" => "jp" } } } # => "It's Godzilla!" ``` The example above shows an example of how you can use all three at the same time. Notice that we have the attribute "lang" defined twice. The `@lang` value takes precedence over the `:attribute![:subtitle]["lang"]` value. gyoku-1.3.1/metadata.yml0000644000175000017500000000477212551205233014535 0ustar globusglobus--- !ruby/object:Gem::Specification name: gyoku version: !ruby/object:Gem::Version version: 1.3.1 platform: ruby authors: - Daniel Harrington autorequire: bindir: bin cert_chain: [] date: 2015-04-05 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: builder requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 2.1.2 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 2.1.2 - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' description: Gyoku translates Ruby Hashes to XML email: me@rubiii.com executables: [] extensions: [] extra_rdoc_files: [] files: - ".gitignore" - ".rspec" - ".travis.yml" - CHANGELOG.md - Gemfile - MIT-LICENSE - README.md - Rakefile - gyoku.gemspec - lib/gyoku.rb - lib/gyoku/array.rb - lib/gyoku/hash.rb - lib/gyoku/version.rb - lib/gyoku/xml_key.rb - lib/gyoku/xml_value.rb - spec/gyoku/array_spec.rb - spec/gyoku/hash_spec.rb - spec/gyoku/xml_key_spec.rb - spec/gyoku/xml_value_spec.rb - spec/gyoku_spec.rb - spec/spec_helper.rb homepage: https://github.com/savonrb/gyoku licenses: - MIT metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 1.9.2 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: gyoku rubygems_version: 2.2.2 signing_key: specification_version: 4 summary: Translates Ruby Hashes to XML test_files: - spec/gyoku/array_spec.rb - spec/gyoku/hash_spec.rb - spec/gyoku/xml_key_spec.rb - spec/gyoku/xml_value_spec.rb - spec/gyoku_spec.rb - spec/spec_helper.rb gyoku-1.3.1/.rspec0000644000175000017500000000001012551205233013324 0ustar globusglobus--color gyoku-1.3.1/.travis.yml0000644000175000017500000000016012551205233014326 0ustar globusglobuslanguage: "ruby" script: "bundle exec rake" rvm: - 1.9.3 - 2.0.0 - 2.1 - 2.2 - jruby-19mode - rbx-2 gyoku-1.3.1/Gemfile0000644000175000017500000000030612551205233013512 0ustar globusglobussource 'https://rubygems.org' gemspec gem 'simplecov', :require => false gem 'coveralls', :require => false platform :rbx do gem 'json' gem 'racc' gem 'rubysl' gem 'rubinius-coverage' end