dslize 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 +20 -0
- data/Gemfile +3 -0
- data/README.md +92 -0
- data/Rakefile +3 -0
- data/dslize.gemspec +20 -0
- data/lib/dslize/dslize.rb +91 -0
- data/lib/dslize/formatter/formatter.rb +16 -0
- data/lib/dslize/formatter/xsd_formatter.rb +91 -0
- data/lib/dslize/version.rb +3 -0
- data/lib/dslize.rb +6 -0
- data/sample.rb +61 -0
- metadata +73 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# DSLize #
|
2
|
+
|
3
|
+
## Summary ##
|
4
|
+
|
5
|
+
Finally an way to generate code with a custom DSL the ruby way !
|
6
|
+
|
7
|
+
### In brief: ###
|
8
|
+
|
9
|
+
* Use real ruby classes to define your DSL
|
10
|
+
* Add custom behaviors defining ruby methods
|
11
|
+
* Create custom formatters/generators extending a base class
|
12
|
+
* Module `DSLize::Methods` defines how your attributes are interpreted, include `DSLize::Methods::Base` to has the standard attributes
|
13
|
+
* Module `DSLize::Definition` defines your DSL
|
14
|
+
|
15
|
+
## Installation ##
|
16
|
+
|
17
|
+
gem install dslize
|
18
|
+
|
19
|
+
then:
|
20
|
+
|
21
|
+
require "dslize"
|
22
|
+
|
23
|
+
## Sample ##
|
24
|
+
|
25
|
+
#! /usr/bin/env ruby
|
26
|
+
|
27
|
+
require 'dslize'
|
28
|
+
|
29
|
+
module DSLize::Methods
|
30
|
+
include DSLize::Methods::Base
|
31
|
+
|
32
|
+
def self.my_first_custom_method(attr, args = {})
|
33
|
+
args[:type] = :custom
|
34
|
+
DSLize.current_object[attr] = args
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.my_second_custom_method
|
38
|
+
DSLize.current_object['baz'] = true
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
module DSLize::Definition
|
44
|
+
|
45
|
+
class City
|
46
|
+
string :name
|
47
|
+
integer :population, :default => 42
|
48
|
+
end
|
49
|
+
|
50
|
+
class Country
|
51
|
+
string :name, :default => 'France'
|
52
|
+
|
53
|
+
has_many City, :as => :cities
|
54
|
+
|
55
|
+
has_one City, :as => :capital
|
56
|
+
end
|
57
|
+
|
58
|
+
class World
|
59
|
+
root
|
60
|
+
|
61
|
+
has_many Country
|
62
|
+
|
63
|
+
my_first_custom_method :foo, :default => 'bar'
|
64
|
+
|
65
|
+
my_second_custom_method
|
66
|
+
end
|
67
|
+
|
68
|
+
class Planet
|
69
|
+
abstract
|
70
|
+
end
|
71
|
+
|
72
|
+
class GazPlanet < Planet
|
73
|
+
end
|
74
|
+
|
75
|
+
class WaterPlanet < Planet
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
class MyCustomFormatter < DSLize::Formatter::Base
|
81
|
+
def do_stuff
|
82
|
+
# with self.object
|
83
|
+
# and self.superclasses
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
DSLize::Formatter::XSD.new.generate!("/tmp/schema.xsd")
|
88
|
+
MyCustomFormatter.new.do_stuff
|
89
|
+
|
90
|
+
## About me ##
|
91
|
+
|
92
|
+
Sylvain UTARD - http://sylvain.utard.info
|
data/Rakefile
ADDED
data/dslize.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "dslize/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "dslize"
|
7
|
+
s.version = DSLize::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ['redox']
|
10
|
+
s.email = ['sylvain.utard@gmail.com']
|
11
|
+
s.homepage = "https://github.com/utard/dslize"
|
12
|
+
s.summary = %q{DSL made easy}
|
13
|
+
s.description = %q{}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.requirements << 'xpain'
|
20
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Object
|
2
|
+
|
3
|
+
def self.method_missing(method_name, *args, &block)
|
4
|
+
return DSLize::Methods.send(method_name, *args) if self.name['DSLize::Definition::'] and DSLize::Methods.respond_to?(method_name)
|
5
|
+
super.method_missing(method_name, args, &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.inherited(subclass)
|
9
|
+
if subclass.name['DSLize::Definition::']
|
10
|
+
superclass = subclass.superclass
|
11
|
+
subclass = subclass.name.split('::').last
|
12
|
+
DSLize.objects ||= {}
|
13
|
+
DSLize.objects[subclass] = {}
|
14
|
+
DSLize.current_object = DSLize.objects[subclass]
|
15
|
+
DSLize.superclasses ||= {}
|
16
|
+
DSLize.superclasses[subclass] = superclass.name.split('::').last
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
module DSLize
|
23
|
+
|
24
|
+
class << self
|
25
|
+
attr_accessor :objects
|
26
|
+
attr_accessor :current_object
|
27
|
+
attr_accessor :superclasses
|
28
|
+
end
|
29
|
+
|
30
|
+
module Methods
|
31
|
+
|
32
|
+
module Base
|
33
|
+
|
34
|
+
def self.included(receiver)
|
35
|
+
receiver.extend(ClassMethods)
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
|
40
|
+
def string(attr, args = {})
|
41
|
+
attribute(:string, attr, args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def integer(attr, args = {})
|
45
|
+
attribute(:integer, attr, args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def double(attr, args = {})
|
49
|
+
attribute(:double, attr, args)
|
50
|
+
end
|
51
|
+
|
52
|
+
def has_many(attr, args = {})
|
53
|
+
relation(:has_many, attr, args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def has_one(attr, args = {})
|
57
|
+
relation(:has_one, attr, args)
|
58
|
+
end
|
59
|
+
|
60
|
+
def root
|
61
|
+
DSLize.current_object[:root] = true
|
62
|
+
end
|
63
|
+
|
64
|
+
def abstract
|
65
|
+
DSLize.current_object[:abstract] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def attribute(type, name, args)
|
70
|
+
args[:type] = type
|
71
|
+
DSLize.current_object[:attributes] = {}
|
72
|
+
DSLize.current_object[:attributes][name] = args
|
73
|
+
end
|
74
|
+
|
75
|
+
def relation(type, name, args)
|
76
|
+
name = (name.name rescue name.to_s).split('::').last
|
77
|
+
args[:type] = name
|
78
|
+
if as = args.delete(:as)
|
79
|
+
name = as.to_s
|
80
|
+
end
|
81
|
+
DSLize.current_object[type] ||= {}
|
82
|
+
DSLize.current_object[type][name] = args
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/formatter'
|
2
|
+
|
3
|
+
module DSLize
|
4
|
+
module Formatter
|
5
|
+
|
6
|
+
class XSD < Base
|
7
|
+
|
8
|
+
def generate!(*args)
|
9
|
+
schema = XPain::Builder.new do |xsd|
|
10
|
+
xsd.schema do
|
11
|
+
objects.each do |name, options|
|
12
|
+
parent = superclasses[name]
|
13
|
+
|
14
|
+
xsd.element(name, :type => (options[:type] or name))
|
15
|
+
|
16
|
+
xsd.complexType({ :name => name, :abstract => options[:abstract], :mixed => false }.select { |k,v| !v.nil? }) do
|
17
|
+
extends_base_if_needed(xsd, parent) do
|
18
|
+
|
19
|
+
# relations
|
20
|
+
xsd.sequence do
|
21
|
+
{ :has_one => nil, :has_many => "unbounded"}.each do |relation, max_occurs|
|
22
|
+
(options[relation] or []).each do |k, v|
|
23
|
+
named_object_if_needed(xsd, k, v) do
|
24
|
+
xsd.element(nil, { :ref => (v[:type] or k), :maxOccurs => max_occurs }.select { |k,v| !v.nil? })
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# attributes
|
31
|
+
(options[:attributes] or []).each do |k, v|
|
32
|
+
xsd.attribute({ :name => k, :default => v[:default], :type => xsd_type(v[:type]) }.select { |k,v| !v.nil? })
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
File.open(args[0], "w") do |f|
|
43
|
+
f << schema.to_xml.gsub(' name=""', '') # xpain is buggy and always write a name on <element>
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def extends_base_if_needed(xsd, parent)
|
49
|
+
if parent != "Object"
|
50
|
+
xsd.complexContent :mixed => false do
|
51
|
+
xsd.extension :base => parent do
|
52
|
+
yield
|
53
|
+
end
|
54
|
+
end
|
55
|
+
else
|
56
|
+
yield
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def named_object_if_needed(xsd, name, options)
|
61
|
+
if options[:type] and name != options[:type]
|
62
|
+
xsd.element name, :contains => 'none' do
|
63
|
+
xsd.complexType do
|
64
|
+
xsd.sequence do
|
65
|
+
yield
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
yield
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def xsd_type(type)
|
75
|
+
case type
|
76
|
+
when :float
|
77
|
+
"xsd:double"
|
78
|
+
when :integer
|
79
|
+
"xsd:int"
|
80
|
+
when :string
|
81
|
+
"xsd:string"
|
82
|
+
else
|
83
|
+
type
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/lib/dslize.rb
ADDED
data/sample.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
# dev mode
|
5
|
+
require File.dirname(__FILE__) + '/lib/dslize'
|
6
|
+
rescue
|
7
|
+
require 'dslize'
|
8
|
+
end
|
9
|
+
|
10
|
+
module DSLize::Methods
|
11
|
+
include DSLize::Methods::Base
|
12
|
+
|
13
|
+
def self.my_first_custom_method(attr, args = {})
|
14
|
+
args[:type] = :custom
|
15
|
+
DSLize.current_object[attr] = args
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.my_second_custom_method
|
19
|
+
DSLize.current_object['baz'] = true
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
module DSLize::Definition
|
25
|
+
|
26
|
+
class City
|
27
|
+
string :name
|
28
|
+
integer :population, :default => 42
|
29
|
+
end
|
30
|
+
|
31
|
+
class Country
|
32
|
+
string :name, :default => 'France'
|
33
|
+
|
34
|
+
has_many City, :as => :cities
|
35
|
+
|
36
|
+
has_one City, :as => :capital
|
37
|
+
end
|
38
|
+
|
39
|
+
class World
|
40
|
+
root
|
41
|
+
|
42
|
+
has_many Country
|
43
|
+
|
44
|
+
my_first_custom_method :foo, :default => 'bar'
|
45
|
+
|
46
|
+
my_second_custom_method
|
47
|
+
end
|
48
|
+
|
49
|
+
class Planet
|
50
|
+
abstract
|
51
|
+
end
|
52
|
+
|
53
|
+
class GazPlanet < Planet
|
54
|
+
end
|
55
|
+
|
56
|
+
class WaterPlanet < Planet
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
DSLize::Formatter::XSD.new.generate!(ARGV[0])
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dslize
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- redox
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2012-07-10 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: ""
|
22
|
+
email:
|
23
|
+
- sylvain.utard@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- Gemfile
|
33
|
+
- README.md
|
34
|
+
- Rakefile
|
35
|
+
- dslize.gemspec
|
36
|
+
- lib/dslize.rb
|
37
|
+
- lib/dslize/dslize.rb
|
38
|
+
- lib/dslize/formatter/formatter.rb
|
39
|
+
- lib/dslize/formatter/xsd_formatter.rb
|
40
|
+
- lib/dslize/version.rb
|
41
|
+
- sample.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: https://github.com/utard/dslize
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
requirements:
|
66
|
+
- xpain
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.3.6
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: DSL made easy
|
72
|
+
test_files: []
|
73
|
+
|