soybean 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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