dsl_maker 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.
@@ -0,0 +1,47 @@
1
+ # This verifies the various error-handling situations.
2
+
3
+ describe "DSL::Maker validation" do
4
+ it "requires a block for :add_entrypoint" do
5
+ expect {
6
+ Class.new(DSL::Maker) do
7
+ add_entrypoint(:pizza)
8
+ end
9
+ }.to raise_error('Block required for add_entrypoint')
10
+ end
11
+
12
+ it "requires a block for :generate_dsl" do
13
+ expect {
14
+ Class.new(DSL::Maker) do
15
+ add_entrypoint(:first, {
16
+ :second => generate_dsl({})
17
+ }) {}
18
+ end
19
+ }.to raise_error('Block required for generate_dsl')
20
+ end
21
+
22
+ it "requires a recognized type for attributes" do
23
+ expect {
24
+ Class.new(DSL::Maker) do
25
+ add_entrypoint(:pizza, {
26
+ :cheese => true,
27
+ }) do
28
+ Pizza.new(cheese, nil, nil, nil)
29
+ end
30
+ end
31
+ }.to raise_error("Unrecognized element type 'true'")
32
+ end
33
+
34
+ it "rejects attributes which block Boolean helper methods" do
35
+ %w(yes no on off __apply).each do |name|
36
+ expect {
37
+ Class.new(DSL::Maker) do
38
+ add_entrypoint(:pizza, {
39
+ name => String,
40
+ }) do
41
+ Pizza.new(cheese, nil, nil, nil)
42
+ end
43
+ end
44
+ }.to raise_error("Illegal attribute name '#{name}'")
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,158 @@
1
+ # This will use a DSL that parses a family tree.
2
+ #
3
+ # Notes:
4
+ # 1. Because we're creating classes on the fly, we must fully-qualify the Boolean
5
+ # class name. If we created real classes, the context would be provided for us.
6
+ describe 'A multi-level DSL making family-trees' do
7
+ Person = Struct.new(:name, :child)
8
+
9
+ it "can handle a simple single-level parse of a two-level DSL" do
10
+ dsl_class = Class.new(DSL::Maker) do
11
+ add_entrypoint(:person, {
12
+ :name => String,
13
+ :child => generate_dsl({
14
+ :name => String,
15
+ }) {
16
+ Person.new(name)
17
+ },
18
+ }) do
19
+ Person.new(name, child)
20
+ end
21
+ end
22
+
23
+ person = dsl_class.parse_dsl('person { name "Tom" }')
24
+ expect(person).to be_instance_of(Person)
25
+ expect(person.name).to eq('Tom')
26
+ expect(person.child).to be_nil
27
+ end
28
+
29
+ it "can handle a two-level parse of a two-level DSL" do
30
+ dsl_class = Class.new(DSL::Maker) do
31
+ add_entrypoint(:person, {
32
+ :name => String,
33
+ :child => generate_dsl({
34
+ :name => String,
35
+ }) {
36
+ Person.new(name, nil)
37
+ },
38
+ }) do
39
+ Person.new(name, child)
40
+ end
41
+ end
42
+
43
+ person = dsl_class.parse_dsl("
44
+ person {
45
+ name 'Tom'
46
+ child {
47
+ name 'Bill'
48
+ }
49
+ }
50
+ ")
51
+ expect(person).to be_instance_of(Person)
52
+ expect(person.name).to eq('Tom')
53
+ expect(person.child).to be_instance_of(Person)
54
+ expect(person.child.name).to eq('Bill')
55
+ end
56
+
57
+ it "can handle a three-level parse of a three-level DSL" do
58
+ dsl_class = Class.new(DSL::Maker) do
59
+ add_entrypoint(:person, {
60
+ :name => String,
61
+ :child => generate_dsl({
62
+ :name => String,
63
+ :child => generate_dsl({
64
+ :name => String,
65
+ }) {
66
+ Person.new(name, nil)
67
+ },
68
+ }) {
69
+ Person.new(name, child)
70
+ },
71
+ }) do
72
+ Person.new(name, child)
73
+ end
74
+ end
75
+
76
+ person = dsl_class.parse_dsl("
77
+ person {
78
+ name 'Tom'
79
+ child {
80
+ name 'Bill'
81
+ child {
82
+ name 'Judith'
83
+ }
84
+ }
85
+ }
86
+ ")
87
+ expect(person).to be_instance_of(Person)
88
+ expect(person.name).to eq('Tom')
89
+ expect(person.child).to be_instance_of(Person)
90
+ expect(person.child.name).to eq('Bill')
91
+ expect(person.child.child).to be_instance_of(Person)
92
+ expect(person.child.child.name).to eq('Judith')
93
+ end
94
+
95
+ it "can define things recursively" do
96
+ dsl_class = Class.new(DSL::Maker) do
97
+ person = generate_dsl({
98
+ :name => String,
99
+ }) {
100
+ Person.new(name, child)
101
+ }
102
+ build_dsl_element(person, :child, person)
103
+
104
+ add_entrypoint(:person, {
105
+ :name => String,
106
+ :child => person,
107
+ }) do
108
+ Person.new(name, child)
109
+ end
110
+ end
111
+
112
+ # This is taken from https://en.wikipedia.org/wiki/Family_tree_of_the_Bible
113
+ person = dsl_class.parse_dsl("
114
+ person {
115
+ name 'Adam'
116
+ child {
117
+ name 'Seth'
118
+ child {
119
+ name 'Enos'
120
+ child {
121
+ name 'Cainan'
122
+ child {
123
+ name 'Mahalaleel'
124
+ child {
125
+ name 'Jared'
126
+ child {
127
+ name 'Enoch'
128
+ child {
129
+ name 'Methuselah'
130
+ child {
131
+ name 'Lamech'
132
+ child {
133
+ name 'Noah'
134
+ child {
135
+ name 'Shem'
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ ")
148
+
149
+ [
150
+ 'Adam', 'Seth', 'Enos', 'Cainan', 'Mahalaleel', 'Jared',
151
+ 'Enoch', 'Methuselah', 'Lamech', 'Noah', 'Shem',
152
+ ].each do |name|
153
+ expect(person).to be_instance_of(Person)
154
+ expect(person.name).to eq(name)
155
+ person = person.child
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,56 @@
1
+ # This will use a DSL that defines Cars
2
+
3
+ describe "A DSL describing cars used with multiple invocations" do
4
+ Car = Struct.new(:maker)
5
+ Truck = Struct.new(:maker)
6
+
7
+ it "returns two items in the right order" do
8
+ dsl_class = Class.new(DSL::Maker) do
9
+ add_entrypoint(:car, {
10
+ :maker => String,
11
+ }) do
12
+ Car.new(maker)
13
+ end
14
+ end
15
+
16
+ cars = dsl_class.parse_dsl("
17
+ car { maker 'Honda' }
18
+ car { maker 'Acura' }
19
+ ")
20
+ expect(cars).to be_instance_of(Array)
21
+ expect(cars.length).to eq(2)
22
+ expect(cars[0]).to be_instance_of(Car)
23
+ expect(cars[0].maker).to eq('Honda')
24
+ expect(cars[1]).to be_instance_of(Car)
25
+ expect(cars[1].maker).to eq('Acura')
26
+ end
27
+
28
+ it "returns items from different entrypoints in the right order" do
29
+ dsl_class = Class.new(DSL::Maker) do
30
+ add_entrypoint(:car, {
31
+ :maker => String,
32
+ }) do
33
+ Car.new(maker)
34
+ end
35
+ add_entrypoint(:truck, {
36
+ :maker => String,
37
+ }) do
38
+ Truck.new(maker)
39
+ end
40
+ end
41
+
42
+ vehicles = dsl_class.parse_dsl("
43
+ truck { maker 'Ford' }
44
+ car { maker 'Honda' }
45
+ truck { maker 'Toyota' }
46
+ ")
47
+ expect(vehicles).to be_instance_of(Array)
48
+ expect(vehicles.length).to eq(3)
49
+ expect(vehicles[0]).to be_instance_of(Truck)
50
+ expect(vehicles[0].maker).to eq('Ford')
51
+ expect(vehicles[1]).to be_instance_of(Car)
52
+ expect(vehicles[1].maker).to eq('Honda')
53
+ expect(vehicles[2]).to be_instance_of(Truck)
54
+ expect(vehicles[2].maker).to eq('Toyota')
55
+ end
56
+ end
@@ -0,0 +1,131 @@
1
+ # This will use the pizza-maker DSL from Docile's documentation.
2
+ # @sauce_level = :extra
3
+ # pizza do
4
+ # cheese yes
5
+ # pepperoni yes
6
+ # sauce @sauce_level
7
+ # end
8
+ #=> #<Pizza:0x00001009dc398 @cheese=true, @pepperoni=true, @bacon=false, @sauce=:extra>
9
+
10
+ # Notes:
11
+ # 1. Because we're creating classes on the fly, we must fully-qualify the Boolean
12
+ # class name. If we created real classes, the context would be provided for us.
13
+ describe 'A single-level DSL for pizza' do
14
+ $toppings = [:cheese, :pepperoni, :bacon, :sauce]
15
+ Pizza = Struct.new(*$toppings)
16
+
17
+ def verify_pizza(pizza, values={})
18
+ expect(pizza).to be_instance_of(Pizza)
19
+ $toppings.each do |topping|
20
+ expect(pizza.send(topping)).to eq(values[topping])
21
+ end
22
+ end
23
+
24
+ it 'makes a blank pizza' do
25
+ dsl_class = Class.new(DSL::Maker) do
26
+ add_entrypoint(:pizza) { Pizza.new }
27
+ end
28
+
29
+ pizza = dsl_class.parse_dsl('pizza')
30
+ verify_pizza(pizza)
31
+ end
32
+
33
+ # This tests all the possible Boolean invocations
34
+ it 'makes a cheese(-less) pizza' do
35
+ dsl_class = Class.new(DSL::Maker) do
36
+ add_entrypoint(:pizza, {
37
+ :cheese => DSL::Maker::Boolean,
38
+ }) do
39
+ Pizza.new(cheese, nil, nil, nil)
40
+ end
41
+ end
42
+
43
+ # There is no way to tell if this is invocation is to set the value or to
44
+ # retrieve the value from within the DSL. Therefore, we assume it's a getter
45
+ # and don't default the value to true. Otherwise, false values wouldn't work.
46
+ pizza = dsl_class.parse_dsl('pizza { cheese }')
47
+ verify_pizza(pizza, :cheese => false)
48
+
49
+ # Test the Ruby booleans and falsey's.
50
+ [ true, false, nil ].each do |cheese|
51
+ pizza = dsl_class.parse_dsl("pizza { cheese #{cheese} }")
52
+ verify_pizza(pizza, :cheese => !!cheese)
53
+ end
54
+
55
+ # Test the true values we provide
56
+ %w(Yes On True yes on).each do |cheese|
57
+ pizza = dsl_class.parse_dsl("pizza { cheese #{cheese} }")
58
+ verify_pizza(pizza, :cheese => true)
59
+ end
60
+
61
+ # Test the false values we provide
62
+ %w(No Off False no off).each do |cheese|
63
+ pizza = dsl_class.parse_dsl("pizza { cheese #{cheese} }")
64
+ verify_pizza(pizza, :cheese => false)
65
+ end
66
+
67
+ # Test the boolean-ized strings we provide
68
+ %w(Yes On True yes on true).each do |cheese|
69
+ pizza = dsl_class.parse_dsl("pizza { cheese '#{cheese}' }")
70
+ verify_pizza(pizza, :cheese => true)
71
+ end
72
+ %w(No Off False no off false nil).each do |cheese|
73
+ pizza = dsl_class.parse_dsl("pizza { cheese '#{cheese}' }")
74
+ verify_pizza(pizza, :cheese => false)
75
+ end
76
+
77
+ # Test some other things which should all be true
78
+ pizza = dsl_class.parse_dsl("pizza { cheese 5 }")
79
+ verify_pizza(pizza, :cheese => true)
80
+ end
81
+
82
+ it 'makes a saucy pizza' do
83
+ dsl_class = Class.new(DSL::Maker) do
84
+ add_entrypoint(:pizza, {
85
+ :sauce => String,
86
+ }) do
87
+ Pizza.new(nil, nil, nil, sauce)
88
+ end
89
+ end
90
+
91
+ [ :extra, 'extra', :none ].each do |level|
92
+ pizza = case level
93
+ when String
94
+ dsl_class.parse_dsl("pizza { sauce '#{level}' }")
95
+ when Symbol
96
+ dsl_class.parse_dsl("pizza { sauce :#{level} }")
97
+ else
98
+ raise "Unexpected class #{level.class}"
99
+ end
100
+ verify_pizza(pizza, :sauce => level.to_s)
101
+ end
102
+ end
103
+
104
+ it 'makes a pizza with everything' do
105
+ dsl_class = Class.new(DSL::Maker) do
106
+ add_entrypoint(:pizza, {
107
+ :cheese => DSL::Maker::Boolean,
108
+ :bacon => DSL::Maker::Boolean,
109
+ :pepperoni => DSL::Maker::Boolean,
110
+ :sauce => String,
111
+ }) do
112
+ Pizza.new(cheese, pepperoni, bacon, sauce)
113
+ end
114
+ end
115
+
116
+ pizza = dsl_class.parse_dsl("
117
+ pizza {
118
+ cheese yes
119
+ pepperoni yes
120
+ bacon no
121
+ sauce :extra
122
+ }
123
+ ")
124
+ verify_pizza(pizza,
125
+ :sauce => 'extra',
126
+ :pepperoni => true,
127
+ :bacon => false,
128
+ :cheese => true,
129
+ )
130
+ end
131
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path('on_what', File.dirname(File.dirname(__FILE__)))
2
+
3
+ RSpec.configure do |config|
4
+ config.expect_with :rspec do |c|
5
+ c.syntax = :expect
6
+ end
7
+ end
8
+
9
+ unless on_1_8?
10
+ begin
11
+ require 'simplecov'
12
+
13
+ SimpleCov.configure do
14
+ add_filter '/spec/'
15
+ add_filter '/vendor/'
16
+ minimum_coverage 100
17
+ refuse_coverage_drop
18
+ end
19
+
20
+ if on_travis?
21
+ require 'codecov'
22
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
23
+ end
24
+
25
+ SimpleCov.start
26
+ rescue LoadError
27
+ puts "Coverage is disabled - install simplecov to enable."
28
+ end
29
+ end
30
+
31
+ require 'dsl/maker'
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dsl_maker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Rob Kinyon
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: docile
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.1.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '10'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '10'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 3.0.0
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 3.0.0
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 3.0.0
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 3.0.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: simplecov
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: yard
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '0.8'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '0.8'
95
+ - !ruby/object:Gem::Dependency
96
+ name: redcarpet
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '3'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '3'
109
+ - !ruby/object:Gem::Dependency
110
+ name: github-markup
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '1.3'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '1.3'
123
+ description: Easy multi-level DSLs, built on top of Docile
124
+ email: rob.kinyon@gmail.com
125
+ executables: []
126
+ extensions: []
127
+ extra_rdoc_files: []
128
+ files:
129
+ - ".gitattributes"
130
+ - ".gitignore"
131
+ - ".rspec"
132
+ - ".travis.yml"
133
+ - ".yardopts"
134
+ - Gemfile
135
+ - LICENSE
136
+ - README.md
137
+ - Rakefile
138
+ - dsl_maker.gemspec
139
+ - lib/dsl/maker.rb
140
+ - lib/dsl/maker/version.rb
141
+ - on_what.rb
142
+ - spec/args_spec.rb
143
+ - spec/error_spec.rb
144
+ - spec/multi_level_spec.rb
145
+ - spec/multiple_invocation_spec.rb
146
+ - spec/single_level_spec.rb
147
+ - spec/spec_helper.rb
148
+ homepage: https://github.com/robkinyon/ruby-dsl-maker
149
+ licenses:
150
+ - GPL2
151
+ metadata: {}
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: 1.9.3
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ requirements: []
167
+ rubyforge_project:
168
+ rubygems_version: 2.4.6
169
+ signing_key:
170
+ specification_version: 4
171
+ summary: Easy multi-level DSLs
172
+ test_files: []