soybean 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 2.0.0
data/bin/soybean CHANGED
@@ -10,13 +10,21 @@ require 'soybean'
10
10
  class Soybean::CLI < Thor
11
11
  include Thor::Actions
12
12
 
13
- desc "classes file_path.xsd", "Generate Ruby classes from XSD"
13
+ desc "types file_path.xsd", "Generate Ruby classes for xsd-schema from file_path.xsd"
14
14
  method_options :quiet => :boolean, :force => :boolean
15
-
16
- def classes(location, destination = 'types')
17
- action Soybean::Actions::GenerateClasses.new(self, location, destination, options)
15
+ def types(location, destination = '.')
16
+ Soybean::Generators::TypesGenerator.new(URI.parse(location)).generate do |filename, content|
17
+ create_file File.join(destination, filename), content, options
18
+ end
18
19
  end
19
20
 
21
+ desc "service [PATH_TO_WSDL] [DESTINATION_DIR]", "Generate Ruby classes from WSDL"
22
+ method_options :quiet => :boolean, :force => :boolean
23
+ def service(wsdl, destination_dir)
24
+ Soybean::Generators::ServiceGenerator.new(destination_dir, wsdl).generate do |filename, content|
25
+ create_file filename, content, options
26
+ end
27
+ end
20
28
  end
21
29
 
22
30
  Soybean::CLI.start
data/lib/soybean.rb CHANGED
@@ -1,11 +1,14 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+ require 'active_support/hash_with_indifferent_access'
3
+ require 'active_support/core_ext/class'
4
+ require 'active_support/core_ext/array/grouping'
1
5
  require 'active_support/deprecation'
2
6
  require 'active_support/dependencies'
7
+ require 'active_support/dependencies/autoload'
8
+
3
9
  ActiveSupport::Dependencies.autoload_paths << File.absolute_path(File.join(File.dirname(__FILE__), '..', 'vendor/soap4r'))
4
10
  ActiveSupport::Dependencies.autoload_paths << File.absolute_path(File.join(File.dirname(__FILE__), '..', 'lib'))
5
11
 
6
12
  module Soybean
7
- # Thor actions
8
- module Actions
9
- autoload :GenerateClasses, 'soybean/actions/generate_classes'
10
- end
13
+ extend ActiveSupport::Autoload
11
14
  end
@@ -0,0 +1,46 @@
1
+ module Soybean
2
+ class ComplexType
3
+ class_attribute :attributes, :instance_reader => true, :instance_writer => true
4
+ self.attributes = []
5
+
6
+ class << self
7
+ def attr_accessor(*attrs)
8
+ self.attributes += attrs
9
+ super
10
+ end
11
+ end
12
+
13
+ def initialize(*args)
14
+ hash = args.extract_options!
15
+ if args.empty?
16
+ init_from_hash(hash)
17
+ else
18
+ init_from_array(args)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ # @param hash [Hash]
25
+ def init_from_hash(hash)
26
+ check_arguments_number!(hash)
27
+ hash.each do |key, val|
28
+ self.send key.to_sym, val
29
+ end
30
+ end
31
+
32
+ # @param arry [Array]
33
+ def init_from_array(arry)
34
+ check_arguments_number! arry
35
+ attributes.each_with_index do |key, i|
36
+ self.send "#{key}=".to_sym, arry.at(i)
37
+ end
38
+ end
39
+
40
+ def check_arguments_number!(args)
41
+ if args.size != attributes.size
42
+ raise ArgumentError, "wrong number of arguments(#{args.size} for #{attributes.size})"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,32 @@
1
+ module Soybean
2
+ class EncodedMappingRegistryCreator < WSDL::SOAP::EncodedMappingRegistryCreator
3
+
4
+ def initialize(definitions, name_creator, modulepath, defined_const, cache)
5
+ @cache = cache
6
+ super(definitions, name_creator, modulepath, defined_const)
7
+ end
8
+
9
+ private
10
+
11
+ def dump_entry(regname, var)
12
+ if @cache.key?(var[:class])
13
+ ''
14
+ else
15
+ @cache[var[:class]] = true
16
+ "#{regname}.register(\n " +
17
+ [
18
+ dump_entry_item(var, :class),
19
+ dump_entry_item(var, :soap_class),
20
+ dump_entry_item(var, :schema_name, :qname),
21
+ dump_entry_item(var, :schema_type, :qname),
22
+ dump_entry_item(var, :is_anonymous),
23
+ dump_entry_item(var, :schema_basetype, :qname),
24
+ dump_entry_item(var, :schema_qualified),
25
+ dump_entry_item(var, :schema_element),
26
+ dump_entry_item(var, :schema_attribute)
27
+ ].compact.join(",\n ") +
28
+ "\n)\n"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ module Soybean
2
+ module Generators
3
+ module BaseGenerator
4
+
5
+ def filename
6
+ "#{name}.rb"
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,100 @@
1
+ module Soybean
2
+ module Generators
3
+ class ClassGenerator < XSD::CodeGen::ClassDef
4
+
5
+ def def_attr(attrname, writable = true, varname = nil)
6
+ unless safevarname?(varname || attrname)
7
+ raise ArgumentError.new("#{varname || attrname} seems to be unsafe")
8
+ end
9
+ @attrdef << [attrname.underscore, writable, varname.underscore]
10
+ end
11
+
12
+ def def_classvar(var, value)
13
+ var = var.sub(/\A@@/, "")
14
+ unless safevarname?(var)
15
+ raise ArgumentError.new("#{var} seems to be unsafe")
16
+ end
17
+ @classvar << [var.underscore, value]
18
+ end
19
+
20
+ def def_method(name, *params)
21
+ return if name == 'initialize'
22
+ super name.underscore, *params.map(&:underscore)
23
+ end
24
+
25
+ def dump
26
+ buf = ""
27
+ unless @requirepath.empty?
28
+ buf << dump_requirepath
29
+ end
30
+ buf << dump_emptyline unless buf.empty?
31
+ package = @name.split(/::/)[0..-2]
32
+ buf << dump_package_def(package) unless package.empty?
33
+ #buf << dump_comment if @comment
34
+ buf << dump_class_def
35
+ spacer = false
36
+ unless @classvar.empty?
37
+ spacer = true
38
+ buf << dump_classvar
39
+ end
40
+ unless @const.empty?
41
+ buf << dump_emptyline if spacer
42
+ spacer = true
43
+ buf << dump_const
44
+ end
45
+ unless @innermodule.empty?
46
+ buf << dump_emptyline # always add 1 empty line
47
+ spacer = true
48
+ buf << dump_innermodule
49
+ end
50
+ unless @code.empty?
51
+ buf << dump_emptyline if spacer
52
+ spacer = true
53
+ buf << dump_code
54
+ end
55
+ unless @attrdef.empty?
56
+ buf << dump_emptyline if spacer
57
+ spacer = true
58
+ buf << dump_attributes
59
+ end
60
+ buf << accessors
61
+ unless @methoddef.empty?
62
+ buf << dump_emptyline if spacer
63
+ spacer = true
64
+ buf << dump_methods
65
+ end
66
+ buf << dump_class_def_end
67
+ buf << dump_package_def_end(package) unless package.empty?
68
+ buf.gsub(/^\s+$/, '')
69
+ end
70
+
71
+ private
72
+
73
+ def dump_class_def
74
+ name = @name.to_s.split(/::/)
75
+ if @baseclass
76
+ "class #{name.last} < #{@baseclass}\n"
77
+ else
78
+ "class #{name.last} < Soybean::ComplexType\n"
79
+ end
80
+ end
81
+
82
+ def accessors
83
+ @attrdef.map do |attrname, *|
84
+ format("attr_accessor #{attrname.to_sym.inspect}\n", 2)
85
+ end.join
86
+ end
87
+
88
+ def dump_attribute(attrname, writable, varname)
89
+ ""
90
+ end
91
+
92
+ def dump_attributes
93
+ ""
94
+ end
95
+
96
+ end
97
+ end
98
+ end
99
+
100
+
@@ -0,0 +1,31 @@
1
+ require 'wsdl/soap/servant_skelton_creator'
2
+
3
+ module Soybean
4
+ module Generators
5
+ class InterfaceGenerator
6
+ class InterfaceCreator < WSDL::SOAP::ServantSkeltonCreator
7
+ def mapped_class_basename(*)
8
+ (@definitions.name.name.gsub(/Service$/, 'Interface') rescue 'BaseInterface')
9
+ end
10
+ end
11
+
12
+ include BaseGenerator
13
+
14
+ attr_reader :name
15
+
16
+ def initialize(wsdl)
17
+ @wsdl = wsdl
18
+ @name = (wsdl.name.name.underscore.gsub(/_service$/, '_interface') rescue 'base_interface')
19
+ end
20
+
21
+ def dir
22
+ 'interfaces'
23
+ end
24
+
25
+ def generate
26
+ InterfaceCreator.new(@wsdl, WSDL::SOAP::ClassNameCreator.new).dump
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,105 @@
1
+ module Soybean
2
+ module Generators
3
+ class MappingGenerator
4
+ class_attribute :mapping_cache, :instance_reader => true, :instance_writer => true
5
+ self.mapping_cache = Hash.new { |hash, key| hash[key] = {} }
6
+
7
+ include BaseGenerator
8
+ include WSDL::SOAP::ClassDefCreatorSupport
9
+
10
+ attr_reader :name
11
+
12
+ def initialize(schema)
13
+ @schema = schema
14
+ @name = name_from_namespace(schema.targetnamespace)
15
+ @name_creator = WSDL::SOAP::ClassNameCreator.new
16
+ @defined_const = {}
17
+ end
18
+
19
+ def dir
20
+ 'mappings'
21
+ end
22
+
23
+ def generate
24
+ m = ModuleDef.new("#{@name.camelize}")
25
+
26
+ varname = 'EncodedRegistry'
27
+ m.def_code(encoded_creator.dump(varname))
28
+
29
+ varname = 'LiteralRegistry'
30
+ m.def_code(literal_creator.dump(varname))
31
+
32
+ @defined_const.each do |ns, tag|
33
+ m.def_const(tag, dq(ns))
34
+ end
35
+
36
+ registry = ModuleDef.new("Mappings", [m])
37
+ registry.def_require("soap/mapping")
38
+ registry.def_code('EncodedRegistry ||= ::SOAP::Mapping::EncodedRegistry.new')
39
+ registry.def_code('LiteralRegistry ||= ::SOAP::Mapping::LiteralRegistry.new')
40
+
41
+ if block_given?
42
+ yield File.join(dir, filename), registry.dump
43
+ else
44
+ registry.dump
45
+ end
46
+ end
47
+
48
+ def literal_creator
49
+ LiteralMappingRegistryCreator.new(@schema, @name_creator, 'Types', @defined_const, mapping_cache[:literal])
50
+ end
51
+
52
+ def encoded_creator
53
+ EncodedMappingRegistryCreator.new(@schema, @name_creator, 'Types', @defined_const, mapping_cache[:encoded])
54
+ end
55
+
56
+ def name_from_namespace(ns)
57
+ name = URI.parse(ns).path.split('/').delete_if { |part| part.empty? || part == 'type' }.last
58
+ (name || 'base').underscore.gsub(/_service$/, '')
59
+ end
60
+
61
+ class ModuleDef < XSD::CodeGen::ModuleDef
62
+ def initialize(name, modules = [])
63
+ super(name)
64
+ @innermodule = modules
65
+ end
66
+
67
+ def dump
68
+ buf = ""
69
+ unless @requirepath.empty?
70
+ buf << dump_requirepath
71
+ end
72
+ buf << dump_emptyline unless buf.empty?
73
+ package = @name.split(/::/)[0..-2]
74
+ buf << dump_package_def(package) unless package.empty?
75
+ buf << dump_comment if @comment
76
+ buf << dump_module_def
77
+ spacer = false
78
+ unless @const.empty?
79
+ buf << dump_emptyline if spacer
80
+ spacer = true
81
+ buf << dump_const
82
+ end
83
+ unless @code.empty?
84
+ buf << dump_emptyline if spacer
85
+ spacer = true
86
+ buf << dump_code
87
+ end
88
+ unless @innermodule.empty?
89
+ buf << dump_emptyline # always add 1 empty line
90
+ spacer = true
91
+ buf << dump_innermodule
92
+ end
93
+ unless @methoddef.empty?
94
+ buf << dump_emptyline if spacer
95
+ spacer = true
96
+ buf << dump_methods
97
+ end
98
+ buf << dump_module_def_end
99
+ buf << dump_package_def_end(package) unless package.empty?
100
+ buf.gsub(/^\s+$/, '')
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,23 @@
1
+ module Soybean
2
+ module Generators
3
+ class ModelGenerator
4
+ include BaseGenerator
5
+
6
+ attr_reader :name
7
+
8
+ def initialize(wsdl)
9
+ @wsdl = wsdl
10
+ @name = (wsdl.name.name.underscore rescue 'base')
11
+ end
12
+
13
+ def dir
14
+ 'models'
15
+ end
16
+
17
+ def generate
18
+ "class %s < %sInterface\nend" % [@name.camelize, @name.camelize.gsub(/Service$/,'')]
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,48 @@
1
+ module Soybean
2
+ module Generators
3
+ class ServiceGenerator
4
+ attr_reader :path, :wsdl_location, :wsdl
5
+
6
+ def initialize(path, wsdl)
7
+ @path, @wsdl_location = Pathname.new(path), wsdl
8
+ @wsdl = import_wsdl
9
+ @schemes = @wsdl.importedschema.keys
10
+ end
11
+
12
+ def schemes
13
+ @schemes.map { |url| TypesGenerator.new(url) }
14
+ end
15
+
16
+ def generate
17
+ (schemes + mappings + interface + model).map do |generator|
18
+ yield path.join(generator.dir, generator.filename), generator.generate
19
+ end
20
+ end
21
+
22
+ def mappings
23
+ schemes.map { |gen| MappingGenerator.new(gen.xsd) }
24
+ end
25
+
26
+ def interface
27
+ [InterfaceGenerator.new(@wsdl)]
28
+ end
29
+
30
+ def model
31
+ [ModelGenerator.new(@wsdl)]
32
+ end
33
+
34
+ protected
35
+
36
+ def dirs
37
+ @dirs ||= %w{types mappings models interfaces}.inject(HashWithIndifferentAccess.new) do |dirs, dir|
38
+ dirs[dir.singularize] = Pathname.new @base.empty_directory(File.join(destination_dir, dir))
39
+ dirs
40
+ end
41
+ end
42
+
43
+ def import_wsdl
44
+ WSDL::Importer.import(@wsdl_location)
45
+ end
46
+ end
47
+ end
48
+ end