xml_schema_mapper 0.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/.gitignore +17 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +8 -0
- data/.idea/modules.xml +9 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/vcs.xml +7 -0
- data/.idea/workspace.xml +459 -0
- data/.idea/xml_schema_mapper.iml +29 -0
- data/.rspec +2 -0
- data/Gemfile +8 -0
- data/Guardfile +9 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/xml_schema_mapper +7 -0
- data/lib/thor/templates/converter_class.erb +13 -0
- data/lib/thor/templates/converter_spec.erb +11 -0
- data/lib/thor/templates/mapper_class.erb +16 -0
- data/lib/thor/templates/mapper_spec.erb +16 -0
- data/lib/thor/xsd_mappers.rb +91 -0
- data/lib/xml_schema_mapper/builder.rb +105 -0
- data/lib/xml_schema_mapper/element.rb +78 -0
- data/lib/xml_schema_mapper/parser.rb +30 -0
- data/lib/xml_schema_mapper/version.rb +3 -0
- data/lib/xml_schema_mapper.rb +77 -0
- data/spec/build_xml_spec.rb +34 -0
- data/spec/builder_spec.rb +126 -0
- data/spec/element_spec.rb +10 -0
- data/spec/fixtures/LocalUserService.xsd +17 -0
- data/spec/fixtures/UserService.xsd +67 -0
- data/spec/fixtures/common.xsd +23 -0
- data/spec/fixtures/get_first_name.xml +13 -0
- data/spec/fixtures/instance1.xml +14 -0
- data/spec/fixtures/user_service.rb +23 -0
- data/spec/parse_xml_spec.rb +36 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/xml_schema_mapper_spec.rb +28 -0
- data/xml_schema_mapper.gemspec +26 -0
- metadata +193 -0
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            # XmlSchemaMapper
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            TODO: Write a gem description
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## Installation
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Add this line to your application's Gemfile:
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                gem 'xml_schema_mapper'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            And then execute:
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                $ bundle
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            Or install it yourself as:
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                $ gem install xml_schema_mapper
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ## Usage
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            TODO: Write usage instructions here
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            ## Contributing
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            1. Fork it
         | 
| 26 | 
            +
            2. Create your feature branch (`git checkout -b my-new-feature`)
         | 
| 27 | 
            +
            3. Commit your changes (`git commit -am 'Added some feature'`)
         | 
| 28 | 
            +
            4. Push to the branch (`git push origin my-new-feature`)
         | 
| 29 | 
            +
            5. Create new Pull Request
         | 
    
        data/Rakefile
    ADDED
    
    
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            <%- if options[:module_name].present? -%>
         | 
| 2 | 
            +
            class <%= options[:module_name] %>::<%= name.camelize %>Converter
         | 
| 3 | 
            +
            <% else -%>
         | 
| 4 | 
            +
            class <%= name.camelize %>Converter
         | 
| 5 | 
            +
            <% end -%>
         | 
| 6 | 
            +
              include ModelConverter
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              converter do
         | 
| 9 | 
            +
              <%-  attributes.each do |attr| -%>
         | 
| 10 | 
            +
                convert_attr :<%= attr.underscore %>
         | 
| 11 | 
            +
              <%- end %>
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| @@ -0,0 +1,11 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            <%- if options[:module_name].present? -%>
         | 
| 4 | 
            +
            describe <%= options[:module_name] %>::<%= name.camelize %> do
         | 
| 5 | 
            +
            <% else -%>
         | 
| 6 | 
            +
            describe <%= name.camelize %> do
         | 
| 7 | 
            +
            <% end -%>
         | 
| 8 | 
            +
              it { should be_a XmlSchemaMapper }
         | 
| 9 | 
            +
              it { _schema.should be_a LibXML::XML::Schema }
         | 
| 10 | 
            +
              it { _type.name.should eql '<%= name %>' }
         | 
| 11 | 
            +
            end
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
            # @note <%= type.annotation.to_s.gsub("\n", "\n# ") %>
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            <%-  elements.each do |element| -%>
         | 
| 5 | 
            +
            # @attr <%= element.name %> [<%= element.type.name %>] <%= element.annotation || element.type.annotation %>
         | 
| 6 | 
            +
            <%- end -%>
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            <%- if options[:module_name].present? -%>
         | 
| 9 | 
            +
            class <%= options[:module_name] %>::<%= name.camelize %>Mapper
         | 
| 10 | 
            +
            <% else -%>
         | 
| 11 | 
            +
            class <%= name.camelize %>Mapper
         | 
| 12 | 
            +
            <% end -%>
         | 
| 13 | 
            +
              include XmlSchemaMapper
         | 
| 14 | 
            +
              schema '<%= schema_path %>'
         | 
| 15 | 
            +
              type '<%= name %>'
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
            # <%= type.annotation.to_s.gsub("\n", "\n# ") %>
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            <%-  elements.each do |element| -%>
         | 
| 5 | 
            +
              # @attr <%= element.name %> [<%= element.type.name %>] <%= element.type.annotation %>
         | 
| 6 | 
            +
            <%- end %>
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            require 'spec_helper'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            <%- if options[:module_name].present? -%>
         | 
| 11 | 
            +
            describe <%= options[:module_name] %>::<%= name.camelize %> do
         | 
| 12 | 
            +
            <% else -%>
         | 
| 13 | 
            +
            describe <%= name.camelize %> do
         | 
| 14 | 
            +
            <% end -%>
         | 
| 15 | 
            +
              it { should be_a ModelConverter }
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,91 @@ | |
| 1 | 
            +
            require "thor"
         | 
| 2 | 
            +
            require "thor/group"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module XsdMappers
         | 
| 5 | 
            +
              class CLI < Thor
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                desc 'mappers path/to/xsd path/to/generate/classes', 'generate classes for xsd types'
         | 
| 8 | 
            +
                method_option :module_name, default: ''
         | 
| 9 | 
            +
                def mappers(schema_path, destination = './')
         | 
| 10 | 
            +
                  schema(schema_path).types.each do |name, type|
         | 
| 11 | 
            +
                    Mappers.new([name, schema_path, destination]).invoke_all
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                desc 'converters path/to/xsd path/to/generate/classes', 'generate converters for xsd types'
         | 
| 16 | 
            +
                method_option :module_name, default: ''
         | 
| 17 | 
            +
                def converters(schema_path, destination = './')
         | 
| 18 | 
            +
                  schema(schema_path).types.each do |name, type|
         | 
| 19 | 
            +
                    Converters.new([name, schema_path, destination], options.merge(attributes: type.elements.keys)).invoke_all
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                protected
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def schema(path)
         | 
| 26 | 
            +
                  @schema ||= LibXML::XML::Schema.cached(path)
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              class Mappers < Thor::Group
         | 
| 32 | 
            +
                include Thor::Actions
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                # Define arguments and options
         | 
| 35 | 
            +
                argument :name
         | 
| 36 | 
            +
                argument :schema_path
         | 
| 37 | 
            +
                argument :destination, default: './'
         | 
| 38 | 
            +
                class_option :module_name, default: ''
         | 
| 39 | 
            +
                class_option :override, default: false
         | 
| 40 | 
            +
                class_option :base_type, default: nil
         | 
| 41 | 
            +
                class_option :force, default: true
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def self.source_root
         | 
| 44 | 
            +
                  File.dirname(__FILE__)
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def create_lib_file
         | 
| 48 | 
            +
                  template('templates/mapper_class.erb', "app/#{destination}/#{name.underscore}_mapper.rb")
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def create_test_file
         | 
| 52 | 
            +
                  test = options[:test_framework] == "test" ? :test : :spec
         | 
| 53 | 
            +
                  with_padding do
         | 
| 54 | 
            +
                    template 'templates/mapper_spec.erb', "spec/#{destination}/#{name.underscore}_mapper_#{test}.rb"
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                protected
         | 
| 59 | 
            +
                def attributes
         | 
| 60 | 
            +
                  options[:attributes]
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def schema
         | 
| 64 | 
            +
                  @schema ||= LibXML::XML::Schema.cached(schema_path)
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                def elements
         | 
| 68 | 
            +
                  @elements ||= type.elements.values
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def type
         | 
| 72 | 
            +
                  @type ||= schema.types[name]
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              class Converters < Mappers
         | 
| 77 | 
            +
                class_option :attributes, type: :array
         | 
| 78 | 
            +
                class_option :force, default: false
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                def create_lib_file
         | 
| 81 | 
            +
                  template('templates/converter_class.erb', "app/#{destination}/#{name.underscore}_converter.rb")
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                def create_test_file
         | 
| 85 | 
            +
                  test = options[:test_framework] == "test" ? :test : :spec
         | 
| 86 | 
            +
                  with_padding do
         | 
| 87 | 
            +
                    template 'templates/converter_spec.erb', "spec/#{destination}/#{name.underscore}_converter_#{test}.rb"
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
            end
         | 
| @@ -0,0 +1,105 @@ | |
| 1 | 
            +
            class NoToXmlError < NoMethodError
         | 
| 2 | 
            +
            end
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module XmlSchemaMapper
         | 
| 5 | 
            +
              class Builder
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                attr_reader :document, :parent
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                delegate :elements, :to => :@klass
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def initialize(source, parent)
         | 
| 12 | 
            +
                  @parent   = parent.is_a?(Nokogiri::XML::Document) ? parent.root : parent
         | 
| 13 | 
            +
                  @document = parent.document
         | 
| 14 | 
            +
                  @source   = source
         | 
| 15 | 
            +
                  @klass    = source.class
         | 
| 16 | 
            +
                  @schema   = @klass._schema
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def build
         | 
| 20 | 
            +
                  elements.each do |element|
         | 
| 21 | 
            +
                    add_element_namespace_to_root!(element)
         | 
| 22 | 
            +
                    node = create_node(element)
         | 
| 23 | 
            +
                    parent << node unless node.content.blank?
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                  self
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def clean!
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                private
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                attr_reader :source, :schema
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def create_node(element)
         | 
| 37 | 
            +
                  setup_namespace(element) do
         | 
| 38 | 
            +
                    element.simple? ? simple_node(element) : complex_node(element)
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def setup_namespace(element)
         | 
| 43 | 
            +
                  yield.tap do |node|
         | 
| 44 | 
            +
                    return if node.nil?
         | 
| 45 | 
            +
                    node.namespace = find_namespace_definition(element.namespace)
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def simple_node(element)
         | 
| 50 | 
            +
                  document.create_element(element.name, element.content_from(source))
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def complex_node(element)
         | 
| 54 | 
            +
                  object            = source.send(element.reader)
         | 
| 55 | 
            +
                  complex_root_node = document.create_element(element.name)
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  if object.is_a?(XmlSchemaMapper)
         | 
| 58 | 
            +
                    complex_node_from_mapper(complex_root_node, object)
         | 
| 59 | 
            +
                  else
         | 
| 60 | 
            +
                    complex_node_from_xml(complex_root_node, object)
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                rescue NoToXmlError
         | 
| 63 | 
            +
                  raise("object of #{source.class}##{element.reader} should respond to :to_xml")
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def complex_node_from_xml(root, object)
         | 
| 67 | 
            +
                  return root if object.nil?
         | 
| 68 | 
            +
                  if object.respond_to?(:to_xml)
         | 
| 69 | 
            +
                    root << object.to_xml
         | 
| 70 | 
            +
                  else
         | 
| 71 | 
            +
                    raise NoToXmlError
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def complex_node_from_mapper(root, object)
         | 
| 76 | 
            +
                  XmlSchemaMapper::Builder.build(object, root).parent
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                def add_element_namespace_to_root!(element)
         | 
| 80 | 
            +
                  if element.namespace
         | 
| 81 | 
            +
                    ns = schema.namespaces.find_by_href(element.namespace)
         | 
| 82 | 
            +
                    raise "prefix for namespace #{element.namespace.inspect} not found" unless ns
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    document.root.add_namespace(ns.prefix, ns.href)
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                def find_namespace_definition(href)
         | 
| 89 | 
            +
                  document.root.namespace_definitions.find { |ns| ns.href == href }
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                # CLASS_METHODS
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                def self.create_document(xtype)
         | 
| 95 | 
            +
                  Nokogiri::XML::Document.new.tap do |doc|
         | 
| 96 | 
            +
                    doc.root = doc.create_element(xtype.name.camelize(:lower))
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def self.build(mapper, parent)
         | 
| 101 | 
            +
                  XmlSchemaMapper::Builder.new(mapper, parent).build
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              end
         | 
| 105 | 
            +
            end
         | 
| @@ -0,0 +1,78 @@ | |
| 1 | 
            +
            Int     = Integer
         | 
| 2 | 
            +
            Double  = Float
         | 
| 3 | 
            +
            Decimal = Integer
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module XmlSchemaMapper
         | 
| 6 | 
            +
              class Element
         | 
| 7 | 
            +
                attr_reader :xsd
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                delegate :name, :namespace, :type, to: :xsd
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def initialize(xsd_element, klass=nil)
         | 
| 12 | 
            +
                  @xsd   = xsd_element
         | 
| 13 | 
            +
                  @klass = klass
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def klass
         | 
| 17 | 
            +
                  @klass ||= begin
         | 
| 18 | 
            +
                    name = (klass_name || base_klass_name)
         | 
| 19 | 
            +
                    name ? (name+"Mapper").constantize : XmlSchemaMapper.default_class
         | 
| 20 | 
            +
                  rescue NameError
         | 
| 21 | 
            +
                    name.constantize
         | 
| 22 | 
            +
                  rescue NameError => e
         | 
| 23 | 
            +
                    raise e, "NotImplimented type #{name.inspect}"
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def simple?
         | 
| 28 | 
            +
                  [
         | 
| 29 | 
            +
                      LibXML::XML::Schema::Types::XML_SCHEMA_TYPE_SIMPLE,
         | 
| 30 | 
            +
                      LibXML::XML::Schema::Types::XML_SCHEMA_TYPE_BASIC
         | 
| 31 | 
            +
                  ].include? @xsd.type.kind
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def content_from(object)
         | 
| 35 | 
            +
                  data = object.send(reader)
         | 
| 36 | 
            +
                  data.respond_to?(:to_xml) ? data.to_xml : data
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def method_name
         | 
| 40 | 
            +
                  name.underscore
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def writer
         | 
| 44 | 
            +
                  "#{method_name}="
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def reader
         | 
| 48 | 
            +
                  "#{method_name}"
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def xpath(xml)
         | 
| 52 | 
            +
                  if namespace
         | 
| 53 | 
            +
                    xml.at_xpath("./foo:#{name}", { 'foo' => @xsd.namespace })
         | 
| 54 | 
            +
                  else
         | 
| 55 | 
            +
                    xml.at_xpath("./#{name}")
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def elements
         | 
| 60 | 
            +
                  @xsd.elements.keys.map(&:to_sym)
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                private
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                def klass_name
         | 
| 66 | 
            +
                  @xsd.type.name.camelize
         | 
| 67 | 
            +
                rescue NameError
         | 
| 68 | 
            +
                  nil
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def base_klass_name
         | 
| 72 | 
            +
                  @xsd.base.name.camelize
         | 
| 73 | 
            +
                rescue NameError
         | 
| 74 | 
            +
                  nil
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module XmlSchemaMapper
         | 
| 2 | 
            +
              class Parser
         | 
| 3 | 
            +
                def initialize(klass)
         | 
| 4 | 
            +
                  @klass = klass
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def parse(xml)
         | 
| 8 | 
            +
                  instance = @klass.new
         | 
| 9 | 
            +
                  xml = document(xml)
         | 
| 10 | 
            +
                  @klass.elements.each do |e|
         | 
| 11 | 
            +
                    if e.simple?
         | 
| 12 | 
            +
                      instance.send(e.writer, content_for(e, xml))
         | 
| 13 | 
            +
                    else
         | 
| 14 | 
            +
                      instance.send(e.writer, e.klass.parse(e.xpath(xml)))
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                  instance
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def content_for(element, xml)
         | 
| 21 | 
            +
                  node = element.xpath(xml)
         | 
| 22 | 
            +
                  node.content if node
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def document(xml)
         | 
| 26 | 
            +
                  return xml if xml.is_a?(Nokogiri::XML::Node)
         | 
| 27 | 
            +
                  Nokogiri::XML(xml).root
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -0,0 +1,77 @@ | |
| 1 | 
            +
            require "xml_schema_mapper/version"
         | 
| 2 | 
            +
            require "xml"
         | 
| 3 | 
            +
            require "nokogiri"
         | 
| 4 | 
            +
            require "active_support/concern"
         | 
| 5 | 
            +
            require "active_support/core_ext/class"
         | 
| 6 | 
            +
            require "active_support/core_ext/module/delegation"
         | 
| 7 | 
            +
            require "virtus"
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            require "xml_schema_mapper/element"
         | 
| 10 | 
            +
            require "xml_schema_mapper/parser"
         | 
| 11 | 
            +
            require "xml_schema_mapper/builder"
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            module XmlSchemaMapper
         | 
| 14 | 
            +
              extend ActiveSupport::Concern
         | 
| 15 | 
            +
              mattr_accessor :default_class
         | 
| 16 | 
            +
              self.default_class = String
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              included do
         | 
| 19 | 
            +
                class_attribute :_schema
         | 
| 20 | 
            +
                class_attribute :_type
         | 
| 21 | 
            +
                class_attribute :elements
         | 
| 22 | 
            +
                self.elements = []
         | 
| 23 | 
            +
                include Virtus
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              module ClassMethods
         | 
| 27 | 
            +
                def schema(location)
         | 
| 28 | 
            +
                  self._schema = LibXML::XML::Schema.cached(location)
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def type(name)
         | 
| 32 | 
            +
                  raise(%Q{call "schema 'path/to/your/File.xsd'" before calling "type"}) unless _schema
         | 
| 33 | 
            +
                  self._type = _schema.types[name]
         | 
| 34 | 
            +
                  define_elements!
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def define_elements!
         | 
| 38 | 
            +
                  _type.elements.values.each do |element|
         | 
| 39 | 
            +
                    e = XmlSchemaMapper::Element.new(element)
         | 
| 40 | 
            +
                    elements << e
         | 
| 41 | 
            +
                    begin
         | 
| 42 | 
            +
                      attr_accessor e.method_name
         | 
| 43 | 
            +
                    rescue
         | 
| 44 | 
            +
                      raise [e.method_name, e.klass, e.type.name].inspect
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def parse(string_or_node)
         | 
| 50 | 
            +
                  return if string_or_node.nil?
         | 
| 51 | 
            +
                  case string_or_node
         | 
| 52 | 
            +
                  when String
         | 
| 53 | 
            +
                    string = File.exist?(string_or_node) ? File.read(string_or_node) : string_or_node
         | 
| 54 | 
            +
                    XmlSchemaMapper::Parser.new(self).parse(string)
         | 
| 55 | 
            +
                  when Nokogiri::XML::Node
         | 
| 56 | 
            +
                    XmlSchemaMapper::Parser.new(self).parse(string_or_node)
         | 
| 57 | 
            +
                  else
         | 
| 58 | 
            +
                    raise(ArgumentError, "param must be a String or Nokogiri::XML::Node, but \"#{string_or_node.inspect}\" given")
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              def element_names
         | 
| 64 | 
            +
                elements.map(&:name)
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              def to_xml(options = {})
         | 
| 68 | 
            +
                xml_document.root.to_xml({:encoding => 'UTF-8'}.merge(options))
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def xml_document
         | 
| 72 | 
            +
                document = XmlSchemaMapper::Builder.create_document(_type)
         | 
| 73 | 
            +
                builder  = XmlSchemaMapper::Builder.new(self, document.root)
         | 
| 74 | 
            +
                builder.build
         | 
| 75 | 
            +
                builder.document
         | 
| 76 | 
            +
              end
         | 
| 77 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            require "spec_helper"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe "Build XML" do
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              context "by GetFirstName" do
         | 
| 6 | 
            +
                let(:xml) { File.read('spec/fixtures/get_first_name.xml') }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                context "valid data" do
         | 
| 9 | 
            +
                  let(:object) do
         | 
| 10 | 
            +
                    o     = Options.new
         | 
| 11 | 
            +
                    o.one = 'one'
         | 
| 12 | 
            +
                    o.two = 'two'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    f        = Filter.new
         | 
| 15 | 
            +
                    f.age    = 50
         | 
| 16 | 
            +
                    f.gender = 'male'
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    g                 = GetFirstName.new
         | 
| 19 | 
            +
                    g.user_identifier = '001'
         | 
| 20 | 
            +
                    g.is_out          = 'a'
         | 
| 21 | 
            +
                    g.zone            = 'b'
         | 
| 22 | 
            +
                    g.filter          = f
         | 
| 23 | 
            +
                    g.options         = o
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    g
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  it "should build xml" do
         | 
| 29 | 
            +
                    object.to_xml.should eql xml
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            end
         | 
| @@ -0,0 +1,126 @@ | |
| 1 | 
            +
            require "spec_helper"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe XmlSchemaMapper::Builder do
         | 
| 4 | 
            +
              let(:options) { o = Options.new; o.one = 'one'; o.two = 'two'; o }
         | 
| 5 | 
            +
              let(:object) do
         | 
| 6 | 
            +
                f = Filter.new; f.age = 50; f.gender = 'male'
         | 
| 7 | 
            +
                g = GetFirstName.new; g.user_identifier = '001'; g.is_out = 'a'; g.zone = 'b'; g.filter = f; g.options = options
         | 
| 8 | 
            +
                g
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              let(:document) { XmlSchemaMapper::Builder.create_document(object._type) }
         | 
| 12 | 
            +
              subject { XmlSchemaMapper::Builder.new(object, document.root) }
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              context 'after initialize' do
         | 
| 15 | 
            +
                its(:source) { should be object }
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              context 'on build' do
         | 
| 19 | 
            +
                let(:element) { stub(namespace: 'http://example.com/common/', name: 'element', simple?: true, content_from: 1) }
         | 
| 20 | 
            +
                let(:complex_element) { stub(namespace: 'http://example.com/common/', name: 'element', simple?: false) }
         | 
| 21 | 
            +
                let(:stub_node) { document.create_element('element') }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                before do
         | 
| 24 | 
            +
                  subject.stub(elements: [element], value: 1)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                it "should create node for each element" do
         | 
| 28 | 
            +
                  subject.should_receive(:create_node).with(element).and_return(stub_node)
         | 
| 29 | 
            +
                  subject.build
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                it "should create simple_node if element#simple?" do
         | 
| 33 | 
            +
                  element.should_receive(:simple?).and_return(true)
         | 
| 34 | 
            +
                  subject.should_receive(:simple_node).and_return(stub_node)
         | 
| 35 | 
            +
                  subject.build
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                it "should create complex_node unless element#simple?" do
         | 
| 39 | 
            +
                  element.should_receive(:simple?).and_return(false)
         | 
| 40 | 
            +
                  subject.should_receive(:complex_node).and_return(stub_node)
         | 
| 41 | 
            +
                  subject.build
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                it "should add namespace to root node" do
         | 
| 45 | 
            +
                  namespace = stub(prefix: 'w', href: 'namespace')
         | 
| 46 | 
            +
                  element.stub(namespace: 'namespace')
         | 
| 47 | 
            +
                  subject.stub_chain('schema.namespaces.find_by_href').and_return(namespace)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  subject.build
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  subject.document.root.namespaces.should include "xmlns:w" => "namespace"
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                it "should raise error when element namespace not found" do
         | 
| 55 | 
            +
                  element.stub(namespace: 'namespace')
         | 
| 56 | 
            +
                  subject.stub_chain('schema.namespaces.find_by_href').and_return(nil)
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  expect { subject.build }.to raise_error("prefix for namespace \"namespace\" not found")
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                it "should set namespace for node" do
         | 
| 62 | 
            +
                  subject.stub(simple_node: stub_node)
         | 
| 63 | 
            +
                  subject.build
         | 
| 64 | 
            +
                  stub_node.namespace.prefix.should eql 'cm'
         | 
| 65 | 
            +
                  stub_node.namespace.href.should eql 'http://example.com/common/'
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                it "should create complex element with namespace" do
         | 
| 69 | 
            +
                  complex_element.stub(reader: 'filter')
         | 
| 70 | 
            +
                  element_node = document.create_element('element')
         | 
| 71 | 
            +
                  age_node = document.create_element('age', '50')
         | 
| 72 | 
            +
                  gender_node = document.create_element('gender', 'male')
         | 
| 73 | 
            +
                  subject.stub(elements: [complex_element])
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  subject.document.should_receive(:create_element).with('element').and_return(element_node)
         | 
| 76 | 
            +
                  subject.document.should_receive(:create_element).with('age', 50).and_return(age_node)
         | 
| 77 | 
            +
                  subject.document.should_receive(:create_element).with('gender', 'male').and_return(gender_node)
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  subject.build
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  element_node.namespace.prefix.should eql 'cm'
         | 
| 82 | 
            +
                  gender_node.namespace.prefix.should eql 'tns'
         | 
| 83 | 
            +
                  age_node.namespace.prefix.should eql 'tns'
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                it "should create complex element without namespace" do
         | 
| 87 | 
            +
                  complex_element.stub(namespace: nil, reader: 'options')
         | 
| 88 | 
            +
                  subject.stub(elements: [complex_element])
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  element_node = document.create_element('element')
         | 
| 91 | 
            +
                  one_node = document.create_element('one', 'one')
         | 
| 92 | 
            +
                  two_node = document.create_element('two', 'two')
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                  subject.document.should_receive(:create_element).with('element').and_return(element_node)
         | 
| 95 | 
            +
                  subject.document.should_receive(:create_element).with('one', 'one').and_return(one_node)
         | 
| 96 | 
            +
                  subject.document.should_receive(:create_element).with('two', 'two').and_return(two_node)
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  subject.build
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  element_node.namespace.should be_nil
         | 
| 101 | 
            +
                  one_node.namespace.should be_nil
         | 
| 102 | 
            +
                  two_node.namespace.should be_nil
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                it "should accept not a XmlSchemaMapper for node" do
         | 
| 106 | 
            +
                  object.stub(not_a_mapper: document.create_element('not_a_mapper'))
         | 
| 107 | 
            +
                  complex_element.stub(namespace: 'http://example.com/common/', reader: 'not_a_mapper')
         | 
| 108 | 
            +
                  subject.stub(elements: [complex_element])
         | 
| 109 | 
            +
                  element_node = document.create_element('element') << object.not_a_mapper.to_xml
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  element_node.should_receive(:<<).with(object.not_a_mapper.to_xml).and_return(element_node)
         | 
| 112 | 
            +
                  subject.document.should_receive(:create_element).with('element').and_return(element_node)
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  subject.build
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                it "should raise error when don't respond to :to_xml'" do
         | 
| 118 | 
            +
                  object.stub(not_a_mapper: [])
         | 
| 119 | 
            +
                  complex_element.stub(namespace: nil, reader: 'not_a_mapper')
         | 
| 120 | 
            +
                  subject.stub(elements: [complex_element])
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                  expect { subject.build }.to raise_error("object of GetFirstName#not_a_mapper should respond to :to_xml")
         | 
| 123 | 
            +
                end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
              end
         | 
| 126 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            <?xml version="1.0" encoding="utf-8"?>
         | 
| 2 | 
            +
            <xs:schema xmlns:tns="http://example.com/UserService/type/" xmlns:xs="http://www.w3.org/2001/XMLSchema"
         | 
| 3 | 
            +
                       targetNamespace="http://example.com/UserService/type/loc" version="1.0">
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              <xs:import namespace="http://example.com/UserService/type/" schemaLocation="UserService.xsd"/>
         | 
| 6 | 
            +
             | 
| 7 | 
            +
             | 
| 8 | 
            +
              <xs:element name="getFirstNameObj">
         | 
| 9 | 
            +
                <xs:complexType>
         | 
| 10 | 
            +
                  <xs:sequence>
         | 
| 11 | 
            +
                    <xs:element ref="tns:getFirstName"/>
         | 
| 12 | 
            +
                    <xs:element ref="tns:getLastName"/>
         | 
| 13 | 
            +
                  </xs:sequence>
         | 
| 14 | 
            +
                </xs:complexType>
         | 
| 15 | 
            +
              </xs:element>
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            </xs:schema>
         |