xmlable 0.0.0.alpha1

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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +245 -0
  4. data/lib/xmlable/attribute.rb +16 -0
  5. data/lib/xmlable/builder.rb +189 -0
  6. data/lib/xmlable/document.rb +16 -0
  7. data/lib/xmlable/element.rb +19 -0
  8. data/lib/xmlable/exports/base.rb +78 -0
  9. data/lib/xmlable/exports/json_exporter.rb +208 -0
  10. data/lib/xmlable/exports/xml_exporter.rb +179 -0
  11. data/lib/xmlable/exports.rb +11 -0
  12. data/lib/xmlable/handlers/attribute.rb +41 -0
  13. data/lib/xmlable/handlers/attribute_none.rb +10 -0
  14. data/lib/xmlable/handlers/base.rb +103 -0
  15. data/lib/xmlable/handlers/document.rb +33 -0
  16. data/lib/xmlable/handlers/element.rb +89 -0
  17. data/lib/xmlable/handlers/element_none.rb +10 -0
  18. data/lib/xmlable/handlers/elements.rb +15 -0
  19. data/lib/xmlable/handlers/mixins/described.rb +19 -0
  20. data/lib/xmlable/handlers/mixins/namespace.rb +40 -0
  21. data/lib/xmlable/handlers/mixins/tag.rb +24 -0
  22. data/lib/xmlable/handlers/namespace.rb +26 -0
  23. data/lib/xmlable/handlers/root.rb +9 -0
  24. data/lib/xmlable/handlers/root_none.rb +10 -0
  25. data/lib/xmlable/handlers/storage.rb +104 -0
  26. data/lib/xmlable/handlers.rb +23 -0
  27. data/lib/xmlable/mixins/attributes_storage.rb +195 -0
  28. data/lib/xmlable/mixins/bare_value.rb +41 -0
  29. data/lib/xmlable/mixins/castable.rb +93 -0
  30. data/lib/xmlable/mixins/container.rb +71 -0
  31. data/lib/xmlable/mixins/content_storage.rb +138 -0
  32. data/lib/xmlable/mixins/document_storage.rb +136 -0
  33. data/lib/xmlable/mixins/elements_storage.rb +219 -0
  34. data/lib/xmlable/mixins/export.rb +45 -0
  35. data/lib/xmlable/mixins/instantiable.rb +47 -0
  36. data/lib/xmlable/mixins/namespace_definitions_storage.rb +105 -0
  37. data/lib/xmlable/mixins/object.rb +95 -0
  38. data/lib/xmlable/mixins/options_storage.rb +39 -0
  39. data/lib/xmlable/mixins/root_storage.rb +162 -0
  40. data/lib/xmlable/mixins/standalone_attribute.rb +34 -0
  41. data/lib/xmlable/mixins/standalone_element.rb +47 -0
  42. data/lib/xmlable/mixins/value_storage.rb +84 -0
  43. data/lib/xmlable/mixins/wrapper.rb +37 -0
  44. data/lib/xmlable/mixins.rb +25 -0
  45. data/lib/xmlable/options/nokogiri_export.rb +19 -0
  46. data/lib/xmlable/options/storage.rb +97 -0
  47. data/lib/xmlable/options.rb +9 -0
  48. data/lib/xmlable/types.rb +31 -0
  49. data/lib/xmlable/version.rb +4 -0
  50. data/lib/xmlable.rb +49 -0
  51. metadata +149 -0
@@ -0,0 +1,95 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # Base class contains base item's methods
5
+ #
6
+ module Object
7
+ # @return [Nokogiri::XML::Node]
8
+ attr_reader :__node
9
+ # @return [XMLable::Handlers::Base]
10
+ attr_reader :__handler
11
+
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ end
15
+
16
+ #
17
+ # Initialize
18
+ #
19
+ # @param [Nokogiri::XML::Node] node item's XML node
20
+ # @param [XMLable::Handlers::Base] handler item's handler
21
+ #
22
+ def initialize(node, handler)
23
+ @__node = node
24
+ @__handler = handler
25
+ __inject_node(@__node, @__handler)
26
+ end
27
+
28
+ #
29
+ # Is this object empty?
30
+ #
31
+ # @api private
32
+ #
33
+ # @return [Boolean]
34
+ #
35
+ def __empty?
36
+ true
37
+ end
38
+
39
+ #
40
+ # Does this object have a nested object with given key
41
+ #
42
+ # @return [Object, false]
43
+ #
44
+ def key?(*)
45
+ false
46
+ end
47
+
48
+ #
49
+ # Get nested object
50
+ #
51
+ def [](*)
52
+ end
53
+
54
+ #
55
+ # get nested object value
56
+ #
57
+ def []=(*)
58
+ end
59
+
60
+ private
61
+
62
+ #
63
+ # Inject XML node and handler with the curren object
64
+ #
65
+ # @param [Nokogiri::XML::Node] node item's XML node
66
+ # @param [XMLable::Handlers::Base] handler item's handler
67
+ #
68
+ # @api private
69
+ #
70
+ def __inject_node(node, handler)
71
+ node.instance_variable_set(:@__handler, handler)
72
+ node.instance_variable_set(:@__element, self)
73
+ end
74
+
75
+ module ClassMethods
76
+ #
77
+ # Get inherited object value if it's set
78
+ #
79
+ # @param [String, Symbol] var varibale name
80
+ #
81
+ # @return [Object, nil]
82
+ #
83
+ def __nested(var)
84
+ klass = superclass
85
+ obj = nil
86
+ loop do
87
+ obj = klass.instance_variable_get(var)
88
+ break if obj || !(klass = klass.superclass)
89
+ end
90
+ obj ? obj.clone : nil
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,39 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # OptionsStorage modules contains method to manage with addtional options
5
+ #
6
+ module OptionsStorage
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ #
13
+ # Set the export options
14
+ #
15
+ def on_export(*args)
16
+ args.each { |opt| __options.opt_set(opt, true) }
17
+ end
18
+
19
+ #
20
+ # Set the before export options
21
+ #
22
+ def before_export(&block)
23
+ __options.opt_set(:before_export, block) if block_given?
24
+ end
25
+
26
+ #
27
+ # Get the options storage
28
+ #
29
+ # @api private
30
+ #
31
+ # @return [XMLable::Options::Strage]
32
+ #
33
+ def __options
34
+ @__options ||= __nested(:@__options) || Options::Storage.new
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,162 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # RootStorage module contains the logic to manage with XML root element
5
+ #
6
+ module RootStorage
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ #
12
+ # Set XML root element
13
+ #
14
+ # @param [Nokogiri::XML::Element, nil] node
15
+ #
16
+ def __set_root(node)
17
+ unless node.is_a?(Nokogiri::XML::Element)
18
+ node = Nokogiri::XML::Element.new(__root_handler.tag.to_s, @__node)
19
+ @__node.root = node
20
+ end
21
+ node.instance_variable_set(:@__handler, __root_handler)
22
+ @root = __root_handler.from_xml_element(node)
23
+ if __root_handler.namespace_prefix
24
+ node.namespace = node.namespace_scopes.find do |n|
25
+ n.prefix == __root_handler.namespace_prefix
26
+ end
27
+ end
28
+ end
29
+
30
+ def method_missing(name, *args, &block)
31
+ return super unless key?(name)
32
+ return root unless name.to_s.end_with?('=')
33
+ self.root = args.first
34
+ end
35
+
36
+ #
37
+ # Is the current object empty?
38
+ #
39
+ # @return [Boolean]
40
+ #
41
+ def __empty?
42
+ return false unless super
43
+ __empty(root)
44
+ end
45
+
46
+ #
47
+ # Get root object
48
+ #
49
+ # @return [XMLable::Mixins::Object]
50
+ #
51
+ def root
52
+ __set_root(nil) unless instance_variable_defined?(:@root)
53
+ @root
54
+ end
55
+
56
+ #
57
+ # Get unwraped root object's value
58
+ #
59
+ # @return [Object]
60
+ #
61
+ def root!
62
+ root.__object
63
+ end
64
+
65
+ #
66
+ # Initialize root object with params
67
+ #
68
+ # @param [XMLable::Handlers::Root, XMLable::Handlers::RootNone] h root's handler
69
+ # @param [Object] value
70
+ #
71
+ def __root_object_initialize(h, value)
72
+ return self.root = value unless value.is_a?(Hash)
73
+ value.each do |key, val|
74
+ root[key].__initialize_with(val)
75
+ end
76
+ end
77
+
78
+ #
79
+ # Set root object content
80
+ #
81
+ # @param [Object] val
82
+ #
83
+ def root=(val)
84
+ @root.__overwrite_content(val)
85
+ end
86
+
87
+ #
88
+ # Does this object has root object with given key?
89
+ #
90
+ # @return [Boolean]
91
+ #
92
+ def key?(key)
93
+ names = ['root', __root_handler.method_name]
94
+ return __root_handler if names.include?(key.to_s)
95
+ super
96
+ end
97
+
98
+ #
99
+ # Get root object by key
100
+ #
101
+ # @param [String] key
102
+ #
103
+ def [](key)
104
+ root if key?(key)
105
+ end
106
+
107
+ #
108
+ # Set root object value
109
+ #
110
+ # @param [String] key
111
+ # @param [Object] val
112
+ #
113
+ def []=(key, val)
114
+ self.root = val if key?(key)
115
+ end
116
+
117
+ #
118
+ # Cureent root object's handler
119
+ #
120
+ # @return [XMLable::Handlers::Root, XMLable::Handlers::RootNone]
121
+ #
122
+ def __root_handler
123
+ self.class.__root_handler
124
+ end
125
+
126
+ module ClassMethods
127
+ #
128
+ # Define root handler
129
+ #
130
+ # @see XMLable::Handler::Base#build
131
+ #
132
+ def root(*args, &block)
133
+ @__root_handler = Handlers::Root.build(*args, &block)
134
+ __define_root(@__root_handler)
135
+ end
136
+
137
+ #
138
+ # Define root object methods
139
+ #
140
+ # @param [XMLable::Handlers::Root] h root's handler
141
+ #
142
+ def __define_root(h)
143
+ return if h.method_name == 'root'
144
+ define_method(h.method_name) { root }
145
+ define_method("#{h.method_name}=") { |val| self.root = val }
146
+ define_method("#{h.method_name}!") { root! }
147
+ define_method("__initialize_#{h.method_name}") { |val| __initialize_root(val) }
148
+ end
149
+
150
+ #
151
+ # Root object's handler
152
+ #
153
+ # @return [XMLable::Handlers::Root, XMLable::Handlers::RootNone]
154
+ #
155
+ def __root_handler
156
+ @__root_handler ||=
157
+ __nested(:@__root_handler) || Handlers::RootNone.build(:root)
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,34 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # StandaloneAttribute module contains standalone attribute's logic
5
+ #
6
+ module StandaloneAttribute
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ #
13
+ # Describe attribute name/tag
14
+ #
15
+ # @param [String, Symbol] tag
16
+ #
17
+ def attr_name(tag)
18
+ @__tag = tag.to_s
19
+ end
20
+
21
+ #
22
+ # Get attribute name/tag
23
+ #
24
+ # @api private
25
+ #
26
+ # @return [String]
27
+ #
28
+ def __tag
29
+ @__tag
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # StandaloneElement module contains standalone element's logic
5
+ #
6
+ module StandaloneElement
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ #
12
+ # @param [Nokogiri::XML::Element] node XML element
13
+ # @param [XMLable::Handlers::Document] handler element's handler
14
+ #
15
+ def initialize(node = nil, handler = nil)
16
+ unless node
17
+ doc = Nokogiri::XML::Document.new
18
+ node = Nokogiri::XML::Element.new(self.class.__tag, doc)
19
+ end
20
+ handler = self.class.__standalone_element_handler unless handler
21
+ super
22
+ end
23
+
24
+ module ClassMethods
25
+ #
26
+ # Describe element's tag name
27
+ #
28
+ # @param [String, Symbol] name
29
+ #
30
+ def tag(name)
31
+ @__tag = name.to_s
32
+ end
33
+
34
+ #
35
+ # Get tag name
36
+ #
37
+ # @api private
38
+ #
39
+ # @return [String]
40
+ #
41
+ def __tag
42
+ @__tag
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,84 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # ValueStorage modules contains the logic to manage XML attributes values
5
+ #
6
+ module ValueStorage
7
+ #
8
+ # Set XML attribute value
9
+ #
10
+ # @param [Nokogiri::XML::Attr] node XML attribute node
11
+ #
12
+ # @api private
13
+ #
14
+ def __set_value(node)
15
+ self.__value = __cast(node.content)
16
+ end
17
+
18
+ #
19
+ # Set value
20
+ #
21
+ # @api private
22
+ #
23
+ # @param [Object] val
24
+ #
25
+ def __value=(val)
26
+ @__value = val
27
+ end
28
+
29
+ #
30
+ # Overwrite XML attribute's value
31
+ #
32
+ # @api private
33
+ #
34
+ # @param [Object] val
35
+ #
36
+ def __overwrite_value(val)
37
+ @__node.content = __export_to_xml(val)
38
+ self.__value = val
39
+ end
40
+
41
+ #
42
+ # Get unwraped attribute object's value
43
+ #
44
+ # @api private
45
+ #
46
+ # @return [Object]
47
+ #
48
+ def __object
49
+ __value
50
+ end
51
+
52
+ #
53
+ #
54
+ # Get attribute value
55
+ #
56
+ # @api private
57
+ #
58
+ # @return [Object]
59
+ #
60
+ def __value
61
+ @__value
62
+ end
63
+
64
+ #
65
+ # Is this element empty?
66
+ #
67
+ # @api private
68
+ #
69
+ # @return [Boolean]
70
+ #
71
+ def __empty?
72
+ return false unless super
73
+ __empty(__value)
74
+ end
75
+
76
+ #
77
+ # @return [String]
78
+ #
79
+ def to_s
80
+ __value.to_s
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,37 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # Wrapper module contains methods to wrap the primitive classes
5
+ #
6
+ module Wrapper
7
+ def method_missing(*args, &block)
8
+ __object.send(*args, &block)
9
+ end
10
+
11
+ #
12
+ # @return [String]
13
+ #
14
+ def to_s(*args)
15
+ __object.to_s(*args)
16
+ end
17
+
18
+ #
19
+ # Is wrapped class inherits from given class?
20
+ #
21
+ # @param [Class] klass
22
+ #
23
+ # @param [Boolean]
24
+ #
25
+ def is_a?(klass)
26
+ __object.is_a?(klass)
27
+ end
28
+
29
+ #
30
+ # @return [String]
31
+ #
32
+ def inspect
33
+ "[Wrapper(#{__object.class})] #{__object.inspect}"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ module XMLable
2
+ #
3
+ # Mixins module contains object mixins modules
4
+ #
5
+ module Mixins
6
+ end
7
+ end
8
+
9
+ require_relative 'mixins/object'
10
+ require_relative 'mixins/standalone_element'
11
+ require_relative 'mixins/standalone_attribute'
12
+ require_relative 'mixins/value_storage'
13
+ require_relative 'mixins/elements_storage'
14
+ require_relative 'mixins/attributes_storage'
15
+ require_relative 'mixins/container'
16
+ require_relative 'mixins/content_storage'
17
+ require_relative 'mixins/document_storage'
18
+ require_relative 'mixins/root_storage'
19
+ require_relative 'mixins/options_storage'
20
+ require_relative 'mixins/namespace_definitions_storage'
21
+ require_relative 'mixins/export'
22
+ require_relative 'mixins/bare_value'
23
+ require_relative 'mixins/instantiable'
24
+ require_relative 'mixins/castable'
25
+ require_relative 'mixins/wrapper'
@@ -0,0 +1,19 @@
1
+ module XMLable
2
+ module Options
3
+ #
4
+ # NokogiriExport class contains Nokogiri's export settings
5
+ #
6
+ # @see http://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/Node/SaveOptions
7
+ #
8
+ class NokogiriExport < Hash
9
+ #
10
+ # Nokogiri's save_with options
11
+ #
12
+ # @return [Nokogiri::XML::Node::SaveOptions.new]
13
+ #
14
+ def save_with
15
+ self[:save_with] ||= Nokogiri::XML::Node::SaveOptions.new
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,97 @@
1
+ module XMLable
2
+ module Options
3
+ #
4
+ # Storage class stores handlers options
5
+ #
6
+ class Storage < Hash
7
+ #
8
+ # Initialize
9
+ #
10
+ # @param [Hash] hash initial params
11
+ #
12
+ def new(hash = {})
13
+ opts_set(hash)
14
+ end
15
+
16
+ #
17
+ # Set multiple options
18
+ #
19
+ # @param [Hash] hash
20
+ #
21
+ def opts_set(hash)
22
+ hash.each { |key, value| opt_set(key, value) }
23
+ end
24
+
25
+ #
26
+ # Set option
27
+ #
28
+ # @param [String, Symbol] key
29
+ # @param [Object] value
30
+ #
31
+ # @return [XMLable::Options::Storage]
32
+ #
33
+ def opt_set(key, value)
34
+ key = key.to_s
35
+ value = false if key =~ /^no_/
36
+ key = key.sub(/^no_/, '').to_sym
37
+ method = "opt_set_#{key}"
38
+ respond_to?(method) ? send(method, value) : opt_default_set(key, value)
39
+ self
40
+ end
41
+
42
+ #
43
+ # Set the drop empty objects
44
+ #
45
+ # @param [Boolean]
46
+ #
47
+ def opt_set_drop_empty(value)
48
+ self[:drop_empty_attributes] = self[:drop_empty_elements] = value
49
+ end
50
+
51
+ #
52
+ # Set the drop undescribed objects
53
+ #
54
+ # @param [Boolean]
55
+ #
56
+ def opt_set_drop_undescribed(value)
57
+ self[:drop_undescribed_attributes] = self[:drop_undescribed_elements] = value
58
+ end
59
+
60
+ #
61
+ # Default option setter
62
+ #
63
+ # @param [String, Symbol] key
64
+ # @param [Object] value
65
+ #
66
+ def opt_default_set(key, value)
67
+ self[key.to_sym] = value
68
+ end
69
+
70
+ %i[undescribed empty].each do |m|
71
+ %i[attributes elements].each do |e|
72
+ define_method("drop_#{m}_#{e}?") { fetch(:"drop_#{m}_#{e}", false) }
73
+ end
74
+ end
75
+
76
+ #
77
+ # Merge options
78
+ #
79
+ # @param [Hash, XMLable::Options::Storage] opts
80
+ #
81
+ # @return [XMLable::Options::Storage]
82
+ #
83
+ def merge_opts(other)
84
+ merger = proc do |_, v1, v2|
85
+ if [v1, v2].all? { |h| h.is_a?(Hash) }
86
+ v1.merge(v2, &merger)
87
+ elsif [v1, v2].all? { |h| h.is_a?(Array) }
88
+ v1 + v2
89
+ else
90
+ v2
91
+ end
92
+ end
93
+ merge(other, &merger)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,9 @@
1
+ module XMLable
2
+ #
3
+ # Options module contains the options handlers
4
+ #
5
+ module Options
6
+ end
7
+ end
8
+ require_relative 'options/storage'
9
+ require_relative 'options/nokogiri_export'
@@ -0,0 +1,31 @@
1
+ XMLable::Builder.tap do |b|
2
+ b.define_type :string, :str, String do
3
+ cast { |val| val.to_s }
4
+ end
5
+
6
+ b.define_type :integer, :int, Integer do
7
+ cast { |val| val.to_i }
8
+ export_to_json { |val| val }
9
+ end
10
+
11
+ b.define_type :float, Float do
12
+ cast { |val| val.to_f }
13
+ export_to_json { |val| val }
14
+ end
15
+
16
+
17
+ b.define_type :date, Date do
18
+ cast do |val|
19
+ Date.parse(val) if val.is_a?(String) && !val.empty?
20
+ end
21
+ export { |val| val.is_a?(Date) ? val.to_s : val }
22
+ end
23
+
24
+ b.define_type :bool, :boolean do
25
+ cast do |val|
26
+ !['false', '0', ''].include?(val.to_s.downcase)
27
+ end
28
+
29
+ export_to_json { |val| val }
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ module XMLable
2
+ # @return [String] returns gem version
3
+ VERSION = '0.0.0.alpha1'
4
+ end