alki-dsl 0.3.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 509cf74d2d757a7d980460a7bd63199e0cfabe39
4
+ data.tar.gz: 33c3f48bac58f19f73f7469ec42f4357cd2321b6
5
+ SHA512:
6
+ metadata.gz: 2ad60f1b2f6c8f012e41c61ff18171ff1fafb65af694c04a96dc15c20ba664c88b60e25d9fc23cf430f7c41396509b7443169ef9f447af2af46e27114255894c
7
+ data.tar.gz: 1245f5d5471641f77603f23aac033fd273f9a622f327fb19ce2268616a9a1c6fb968d589e602f1294a28f69596d0a786242e035d8d5cdbe8bfdc9decca40ec40
data/.gitignore ADDED
@@ -0,0 +1,53 @@
1
+ # Created by .ignore support plugin (hsz.mobi)
2
+ ### Ruby template
3
+ *.gem
4
+ *.rbc
5
+ /.config
6
+ /coverage/
7
+ /InstalledFiles
8
+ /pkg/
9
+ /spec/reports/
10
+ /spec/examples.txt
11
+ /test/tmp/
12
+ /test/version_tmp/
13
+ /tmp/
14
+
15
+ # Used by dotenv library to load environment variables.
16
+ # .env
17
+
18
+ ## Specific to RubyMotion:
19
+ .dat*
20
+ .repl_history
21
+ build/
22
+ *.bridgesupport
23
+ build-iPhoneOS/
24
+ build-iPhoneSimulator/
25
+
26
+ ## Specific to RubyMotion (use of CocoaPods):
27
+ #
28
+ # We recommend against adding the Pods directory to your .gitignore. However
29
+ # you should judge for yourself, the pros and cons are mentioned at:
30
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
31
+ #
32
+ # vendor/Pods/
33
+
34
+ ## Documentation cache and generated files:
35
+ /.yardoc/
36
+ /_yardoc/
37
+ /doc/
38
+ /rdoc/
39
+
40
+ ## Environment normalization:
41
+ /.bundle/
42
+ /vendor/bundle
43
+ /lib/bundler/man/
44
+
45
+ # for a library or gem, you might want to ignore these files since the code is
46
+ # intended to run in multiple environments; otherwise, check them in:
47
+ Gemfile.lock
48
+ # .ruby-version
49
+ # .ruby-gemset
50
+
51
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
52
+ .rvmrc
53
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/alki-dsl.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ require_relative 'lib/alki/dsl/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "alki-dsl"
6
+ spec.version = Alki::Dsl::VERSION
7
+ spec.authors = ["Matt Edlefsen"]
8
+ spec.email = ["matt.edlefsen@gmail.com"]
9
+ spec.summary = %q{Alki dsl library}
10
+ spec.description = %q{Library for defining and using DSLs}
11
+ spec.homepage = "https://github.com/medlefsen/alki-dsl"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = `git ls-files -z`.split("\x0")
15
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
16
+ spec.bindir = 'exe'
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency 'alki-support', '~> 0.3'
21
+ spec.add_development_dependency 'minitest', '~> 5.9', '>= 5.9.1'
22
+ end
@@ -0,0 +1,132 @@
1
+ require 'forwardable'
2
+ require 'alki/support'
3
+
4
+ module Alki
5
+ class ClassBuilder
6
+ def self.create_constant(name,value = Class.new, parent=nil)
7
+ parent ||= Object
8
+ *ans, ln = name.to_s.split('::')
9
+ ans.each do |a|
10
+ unless parent.const_defined? a
11
+ parent.const_set a, Module.new
12
+ end
13
+ parent = parent.const_get a
14
+ end
15
+
16
+ parent.const_set ln, value
17
+ end
18
+
19
+ def self.build(data)
20
+ class_name = data[:class_name]
21
+ if !class_name && data[:name] && data[:prefix]
22
+ class_name = Alki::Support.classify(
23
+ data[:prefix].empty? ? data[:name] : "#{data[:prefix]}/#{data[:name]}"
24
+ )
25
+ end
26
+
27
+ if data[:type] == :module
28
+ klass = Module.new
29
+ else
30
+ super_class = if data[:super_class]
31
+ Alki::Support.load_class data[:super_class]
32
+ else
33
+ Object
34
+ end
35
+ klass = Class.new super_class
36
+ end
37
+ build_class data, klass
38
+ if class_name
39
+ create_constant class_name, klass, data[:parent_class]
40
+ end
41
+ if data[:secondary_classes]
42
+ data[:secondary_classes].each do |data|
43
+ if data[:subclass]
44
+ data = data.merge(parent_class: klass,class_name: data[:subclass])
45
+ elsif !data[:class_name] && !data[:name]
46
+ raise ArgumentError.new("Secondary classes must have names")
47
+ end
48
+ build data
49
+ end
50
+ end
51
+ klass
52
+ end
53
+
54
+ def self.module_not_empty?(mod)
55
+ not mod.instance_methods.empty? &&
56
+ mod.private_instance_methods.empty?
57
+ end
58
+
59
+ def self.build_class(data,klass)
60
+ if data[:module]
61
+ if module_not_empty? data[:module]
62
+ klass.include data[:module]
63
+ end
64
+ if data[:module].const_defined?(:ClassMethods) &&
65
+ module_not_empty?(data[:module]::ClassMethods)
66
+ klass.extend data[:module]::ClassMethods
67
+ end
68
+ end
69
+
70
+ if data[:modules]
71
+ data[:modules].each do |mod|
72
+ klass.include mod
73
+ end
74
+ end
75
+ if data[:class_modules]
76
+ data[:class_modules].each do |mod|
77
+ klass.extend mod
78
+ end
79
+ end
80
+
81
+ add_methods klass, data
82
+ add_initialize klass, data[:initialize_params] if data[:initialize_params]
83
+ end
84
+
85
+ def self.add_methods(klass, data)
86
+ if data[:class_methods]
87
+ data[:class_methods].each do |name, method|
88
+ klass.define_singleton_method name, &method[:body]
89
+ klass.singleton_class.send :private, name if method[:private]
90
+ end
91
+ end
92
+
93
+ if data[:instance_methods]
94
+ data[:instance_methods].each do |name, method|
95
+ klass.send :define_method, name, &method[:body]
96
+ klass.send :private, name if method[:private]
97
+ end
98
+ end
99
+
100
+ if data[:delegators]
101
+ klass.extend Forwardable
102
+ data[:delegators].each do |name,delegator|
103
+ klass.def_delegator delegator[:accessor], delegator[:method], name
104
+ end
105
+ end
106
+
107
+ klass.send :attr_reader, *data[:attr_readers] if data[:attr_readers]
108
+ klass.send :attr_writer, *data[:attr_writers] if data[:attr_writers]
109
+ klass.send :attr_accessor, *data[:attr_accessors] if data[:attr_accessors]
110
+ end
111
+
112
+ def self.add_initialize(klass,params)
113
+ at_setters = ''
114
+ params.each do |(p,default)|
115
+ if default
116
+ default_method = "_default_#{p}".to_sym
117
+ klass.send :define_method, default_method do
118
+ default
119
+ end
120
+ klass.send :private, default_method
121
+ at_setters << "@#{p} = #{p} || #{default_method}\n"
122
+ else
123
+ at_setters << "@#{p} = #{p}\n"
124
+ end
125
+ end
126
+
127
+ klass.class_eval "
128
+ def initialize(#{params.map{|p| p[1] ? "#{p[0]}=nil" : p[0]}.join(', ')})
129
+ #{at_setters}end"
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,45 @@
1
+ require 'alki/support'
2
+ require 'alki/dsl/evaluator'
3
+
4
+ module Alki
5
+ module Dsl
6
+ class Base
7
+ def self.build(data={},&blk)
8
+ Alki::Dsl::Evaluator.evaluate self, data, &blk
9
+ end
10
+
11
+ def self.generate(ctx)
12
+ obj = new(ctx)
13
+ result = {methods: {}}
14
+ info = self.dsl_info
15
+
16
+ result[:init] = obj.method(info[:init]) if info[:init]
17
+ result[:finish] = obj.method(info[:finish]) if info[:finish]
18
+ result[:requires] = info[:requires] if info[:requires]
19
+ result[:helpers] = info[:helpers] if info[:helpers]
20
+
21
+ if info[:methods]
22
+ info[:methods].each do |method|
23
+ if method.is_a?(Array)
24
+ name, method = method
25
+ else
26
+ name = method
27
+ end
28
+ result[:methods][name] = obj.method method
29
+ end
30
+ end
31
+ result
32
+ end
33
+
34
+ def self.dsl_info
35
+ {}
36
+ end
37
+
38
+ def initialize(ctx)
39
+ @ctx = ctx
40
+ end
41
+
42
+ attr_reader :ctx
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,42 @@
1
+ require 'alki/support'
2
+ require 'alki/dsl/evaluator'
3
+
4
+ module Alki
5
+ module Dsl
6
+ class Builder
7
+ def self.build(data,&blk)
8
+ result = Alki::Dsl::Evaluator.evaluate _dsls,data,&blk
9
+ if _processor
10
+ _processor.build result
11
+ else
12
+ result
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def self.dsl(name)
19
+ klass = Alki::Support.load_class name
20
+ unless klass
21
+ raise "Unable to load class #{name.inspect}"
22
+ end
23
+ dsls = _dsls
24
+ dsls += [klass]
25
+ define_singleton_method(:_dsls) { dsls }
26
+ end
27
+
28
+ def self.processor(name)
29
+ klass = Alki::Support.load_class name
30
+ define_singleton_method(:_processor) { klass }
31
+ end
32
+
33
+ def self._dsls
34
+ []
35
+ end
36
+
37
+ def self._processor
38
+ nil
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,86 @@
1
+ module Alki
2
+ module Dsl
3
+ module ClassHelpers
4
+ def class_builder(subclass = nil)
5
+ unless @ctx[:class_builder]
6
+ @ctx[:class_builder] = {}
7
+ %i(module name prefix).each do |attr|
8
+ @ctx[:class_builder][attr] = @ctx[attr] if @ctx[attr]
9
+ end
10
+ end
11
+ if subclass
12
+ scs = @ctx[:class_builder][:secondary_classes] ||= []
13
+ cb = scs.find { |sc| sc[:subclass] == subclass }
14
+ unless cb
15
+ cb = { subclass: subclass }
16
+ scs << cb
17
+ end
18
+ cb
19
+ else
20
+ @ctx[:class_builder]
21
+ end
22
+ end
23
+
24
+ def create_as_module(subclass: nil)
25
+ class_builder(subclass)[:type] = :module
26
+ end
27
+
28
+ def set_super_class(klass,subclass: nil)
29
+ class_builder(subclass)[:super_class] = klass
30
+ end
31
+
32
+ def add_method(name,context:nil,private: false,subclass: nil, &blk)
33
+ class_builder(subclass)[:instance_methods] ||= {}
34
+ class_builder(subclass)[:instance_methods][name.to_sym] = {
35
+ body: blk,
36
+ context: context,
37
+ private: private
38
+ }
39
+ end
40
+
41
+ def add_class_method (name,context: nil,private: false,subclass: nil,&blk)
42
+ class_builder(subclass)[:class_methods] ||= {}
43
+ class_builder(subclass)[:class_methods][name.to_sym] = {
44
+ body: blk,
45
+ context: context,
46
+ private: private
47
+ }
48
+ end
49
+
50
+ def add_helper(name,&blk)
51
+ class_builder('Helpers')[:type] = :module
52
+ add_method name, &blk
53
+ add_method name, subclass: 'Helpers', &blk
54
+ end
55
+
56
+ def add_helper_module(mod)
57
+ class_builder('Helpers')[:type] = :module
58
+ add_module mod
59
+ add_module mod, subclass: 'Helpers'
60
+ end
61
+
62
+ def add_initialize_param(name,default=nil,subclass: nil)
63
+ (class_builder(subclass)[:initialize_params]||=[]) << [name.to_sym,default]
64
+ end
65
+
66
+ def add_instance_class_proxy(type, name,subclass: nil)
67
+ (class_builder(subclass)[:instance_class]||={})[name.to_sym] = {type: type}
68
+ end
69
+
70
+ def add_module(mod,subclass: nil)
71
+ (class_builder(subclass)[:modules]||=[]) << mod
72
+ end
73
+
74
+ def add_accessor(name,type: :accessor,subclass: nil)
75
+ (class_builder(subclass)["attr_#{type}s".to_sym]||=[]) << name
76
+ end
77
+
78
+ def add_delegator(name,accessor,method=name,subclass: nil)
79
+ (class_builder(subclass)[:delegators]||={})[name] = {
80
+ accessor: accessor,
81
+ method: method
82
+ }
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,67 @@
1
+ require 'set'
2
+ require 'alki/support'
3
+
4
+ module Alki
5
+ module Dsl
6
+ class Evaluator
7
+ def initialize
8
+ @inits = []
9
+ @finishers = []
10
+ @processors = []
11
+ @dsls_seen = Set.new
12
+ end
13
+
14
+ def evaluate(dsl,data={},&blk)
15
+ mod = (data[:module] ||= Module.new)
16
+ process_dsl dsl, data
17
+
18
+ @inits.each(&:call)
19
+ dsl_exec mod, &blk
20
+ @finishers.reverse_each(&:call)
21
+ clear_dsl_methods mod
22
+
23
+ @processors.each do |processor|
24
+ processor.build data
25
+ end
26
+
27
+ data
28
+ end
29
+
30
+ def process_dsl(dsl,data)
31
+ return unless @dsls_seen.add? dsl
32
+ cbs = dsl.generate(data)
33
+ if cbs[:requires]
34
+ cbs[:requires].each do |required_dsl|
35
+ process_dsl Alki::Support.load_class(required_dsl), data
36
+ end
37
+ end
38
+ @inits << cbs[:init] if cbs[:init]
39
+ @finishers << cbs[:finish] if cbs[:finish]
40
+ @processors << cbs[:processors] if cbs[:processors]
41
+ if cbs[:methods]
42
+ cbs[:methods].each do |name, proc|
43
+ define_dsl_method data[:module], name, &proc
44
+ end
45
+ end
46
+ end
47
+
48
+ def define_dsl_method(mod,name,&blk)
49
+ mod.define_singleton_method name, &blk
50
+ end
51
+
52
+ def clear_dsl_methods(mod)
53
+ mod.singleton_methods do |m|
54
+ mod.singleton_class.send :remove_method, m
55
+ end
56
+ end
57
+
58
+ def dsl_exec(mod,&blk)
59
+ mod.class_exec &blk
60
+ end
61
+
62
+ def self.evaluate(dsl, data={}, &blk)
63
+ new.evaluate dsl, data, &blk
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,28 @@
1
+ require 'alki/support'
2
+ require 'alki/dsl'
3
+
4
+ module Alki
5
+ module Dsl
6
+ class Loader
7
+ def initialize(root_dir)
8
+ @root_dir = root_dir
9
+ end
10
+
11
+ def all_paths
12
+ Dir[File.join(@root_dir,'**','*.rb')].map do |path|
13
+ path.gsub(File.join(@root_dir,''),'').gsub(/\.rb$/,'')
14
+ end
15
+ end
16
+
17
+ def load_all
18
+ all_paths.inject({}) do |h,path|
19
+ h.merge!(path => Alki::Dsl.load(path))
20
+ end
21
+ end
22
+
23
+ def load(file)
24
+ Alki::Dsl.load File.expand_path("#{file}.rb",@root_dir)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,66 @@
1
+ require 'alki/dsl/loader'
2
+ require 'alki/support'
3
+
4
+ module Alki
5
+ module Dsl
6
+ module Registry
7
+ @registered_paths = {}
8
+ @registered_dirs = {}
9
+
10
+ def self.register(path,builder,**data)
11
+ @registered_paths[File.absolute_path(path)] = Entry.new(builder,data)
12
+ end
13
+
14
+ def self.register_dir(dir_path,builder,**data)
15
+ @registered_dirs[File.join(File.absolute_path(dir_path),'')] = [builder,data]
16
+ end
17
+
18
+ def self.lookup(path, load_configs: true)
19
+ path = File.absolute_path path
20
+ entry = @registered_paths[path]
21
+ return entry if entry
22
+
23
+ @registered_dirs.each do |dir,(builder,data)|
24
+ if path.start_with? dir
25
+ data = {name: Alki::Support.path_name(path, dir)}.merge data
26
+ return Entry.new(builder,data)
27
+ end
28
+ end
29
+
30
+ if load_configs
31
+ root = Alki::Support.find_root(path) do |dir|
32
+ File.exists?(File.join(dir,'config','dsls.rb'))
33
+ end
34
+ if root
35
+ config_file = File.join(root,'config','dsls.rb')
36
+ register config_file, 'alki/dsls/dsl_config', root: root
37
+ require config_file
38
+ return lookup path, load_configs: false
39
+ end
40
+ end
41
+
42
+ nil
43
+ end
44
+
45
+ def self.build(path,&blk)
46
+ entry = lookup path
47
+ if entry
48
+ entry.build blk
49
+ else
50
+ nil
51
+ end
52
+ end
53
+
54
+ class Entry
55
+ def initialize(builder,data)
56
+ @builder = builder
57
+ @data = data
58
+ end
59
+
60
+ def build(blk)
61
+ Alki::Support.load_class(@builder).build @data, &blk
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,5 @@
1
+ module Alki
2
+ module Dsl
3
+ VERSION = '0.3.0'
4
+ end
5
+ end
data/lib/alki/dsl.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'alki/dsl/evaluator'
2
+ require 'alki/dsl/registry'
3
+
4
+ module Alki
5
+ module Dsl
6
+ @loaded = {}
7
+ def self.[]=(path,value)
8
+ @loaded[path] =value
9
+ end
10
+
11
+ def self.[](path)
12
+ @loaded[path]
13
+ end
14
+
15
+ def self.register(*args)
16
+ Alki::Dsl::Registry.register *args
17
+ end
18
+
19
+ def self.register_dir(*args)
20
+ Alki::Dsl::Registry.register_dir *args
21
+ end
22
+
23
+ def self.load(path)
24
+ path = File.absolute_path(path)
25
+ require path
26
+ self[path]
27
+ end
28
+
29
+ def self.build(name,data={},&blk)
30
+ Alki::Support.load_class(name).build data, &blk
31
+ end
32
+ end
33
+ end
34
+
35
+ module Kernel
36
+ def Alki(builder=nil,&blk)
37
+ if blk
38
+ path = caller_locations(1,1)[0].absolute_path
39
+ result = if builder
40
+ builder.build({}, &blk)
41
+ else
42
+ Alki::Dsl::Registry.build path, &blk
43
+ end
44
+ Alki::Dsl[path] = result
45
+ end
46
+ ::Alki
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ require 'alki/dsl/base'
2
+ require 'alki/dsl/class_helpers'
3
+ require 'alki/class_builder'
4
+
5
+ module Alki
6
+ module Dsls
7
+ class Class < Alki::Dsl::Base
8
+ include Alki::Dsl::ClassHelpers
9
+
10
+ self::Helpers = Alki::Dsl::ClassHelpers
11
+
12
+ def self.dsl_info
13
+ {
14
+ methods: %i(class_methods),
15
+ finish: :finish
16
+ }
17
+ end
18
+
19
+ def class_methods(&blk)
20
+ unless ctx[:module].const_defined? :ClassMethods
21
+ ctx[:module].const_set :ClassMethods, Module.new
22
+ end
23
+ ctx[:module]::ClassMethods.class_exec &blk
24
+ end
25
+
26
+ def finish
27
+ ctx[:class] = Alki::ClassBuilder.build class_builder
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,78 @@
1
+ require 'alki/dsl/base'
2
+ require 'alki/support'
3
+ require 'alki/dsl/class_helpers'
4
+
5
+ module Alki
6
+ module Dsls
7
+ class Dsl < Alki::Dsl::Base
8
+ include Alki::Dsl::ClassHelpers
9
+ Helpers = Alki::Dsl::ClassHelpers
10
+
11
+ def self.dsl_info
12
+ {
13
+ requires: ['alki/dsls/class'],
14
+ methods: [
15
+ :dsl_method,
16
+ [:init,:dsl_init],
17
+ [:finish,:dsl_finish],
18
+ :require_dsl,
19
+ :helper,
20
+ :helper_module
21
+ ],
22
+ init: :init,
23
+ finish: :finish
24
+ }
25
+ end
26
+
27
+ def init
28
+ @info = {
29
+ methods: [],
30
+ requires: []
31
+ }
32
+ @helper_modules = []
33
+ @helpers = {}
34
+ end
35
+
36
+ def dsl_method(name, &blk)
37
+ method_name = name.to_sym
38
+ add_method method_name, private: true, &blk
39
+ @info[:methods] << [name.to_sym,method_name]
40
+ end
41
+
42
+ def dsl_init(&blk)
43
+ add_method :_dsl_init, private: true, &blk
44
+ @info[:init] = :_dsl_init
45
+ end
46
+
47
+ def dsl_finish(&blk)
48
+ add_method :_dsl_finish, private: true, &blk
49
+ @info[:finish] = :_dsl_finish
50
+ end
51
+
52
+ def require_dsl(dsl)
53
+ dsl_class = Alki::Support.load_class(dsl)
54
+ @info[:requires] << dsl_class
55
+ if defined? dsl_class::Helpers
56
+ add_module dsl_class::Helpers
57
+ add_helper_module dsl_class::Helpers
58
+ end
59
+ end
60
+
61
+ def helper(name,&blk)
62
+ add_helper name, &blk
63
+ end
64
+
65
+ def helper_module(mod)
66
+ add_helper_module mod
67
+ end
68
+
69
+ def finish
70
+ set_super_class 'alki/dsl/base'
71
+ info = @info.freeze
72
+ add_class_method :dsl_info do
73
+ info
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,28 @@
1
+ require 'alki/dsl/base'
2
+
3
+ module Alki
4
+ module Dsls
5
+ class DslConfig < Alki::Dsl::Base
6
+ def self.dsl_info
7
+ {
8
+ methods: %i(register register_dir register_lib_dir)
9
+ }
10
+ end
11
+
12
+ def register(path,*args)
13
+ path = File.expand_path(path,@ctx[:root])
14
+ Alki::Dsl::Registry.register path, *args
15
+ end
16
+
17
+ def register_dir(path,*args)
18
+ path = File.expand_path(path,@ctx[:root])
19
+ Alki::Dsl::Registry.register_dir path, *args
20
+ end
21
+
22
+ def register_lib_dir(prefix,dsl,**data)
23
+ path = File.join(@ctx[:root],'lib', prefix)
24
+ Alki::Dsl::Registry.register_dir path, dsl, data.merge(prefix: prefix)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ require_relative '../test_helper'
2
+ require 'alki/dsl'
3
+
4
+ describe 'dsl configuration' do
5
+ before do
6
+ $LOAD_PATH.unshift fixture_path('example','lib')
7
+ end
8
+
9
+ it 'should be automatically loaded when requiring a dsl' do
10
+ require 'alki_test/dsls/number'
11
+ AlkiTest::Dsls::Number.must_be_instance_of Class
12
+ AlkiTest::Dsls::Number.singleton_methods.must_include :generate
13
+ end
14
+
15
+ it 'should allow using dsls specified in same dsl config' do
16
+ Kernel.load(fixture_path('example','numbers','three.rb'))
17
+ AlkiTest::Numbers::Three.new.must_equal 3
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ Alki do
2
+ register_dir 'numbers', 'alki_test/dsls/number', prefix: 'alki_test/numbers'
3
+ register_lib_dir 'alki_test/dsls', 'alki/dsls/dsl'
4
+ end
@@ -0,0 +1,11 @@
1
+ Alki do
2
+ require_dsl 'alki_test/dsls/value'
3
+
4
+ init do
5
+ self.value = 0
6
+ end
7
+
8
+ dsl_method :succ do
9
+ self.value += 1
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ Alki do
2
+ require_dsl 'alki/dsls/class'
3
+
4
+ helper :value= do |v|
5
+ ctx[:value] = v
6
+ end
7
+
8
+ helper :value do
9
+ ctx[:value]
10
+ end
11
+
12
+ finish do
13
+ create_as_module
14
+ value = ctx[:value]
15
+ add_class_method :new do
16
+ value
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ Alki do
2
+ succ
3
+ succ
4
+ succ
5
+ end
@@ -0,0 +1,148 @@
1
+ require_relative '../test_helper'
2
+ require 'alki/class_builder'
3
+
4
+ describe Alki::ClassBuilder do
5
+ describe :build do
6
+ def build(data={})
7
+ Alki::ClassBuilder.build data
8
+ end
9
+
10
+ it 'should create and return a new class' do
11
+ build.must_be_instance_of Class
12
+ end
13
+
14
+ it 'should allow creation of modules' do
15
+ build(type: :module).must_be_instance_of Module
16
+ end
17
+
18
+ it 'should allow setting instance methods' do
19
+ obj = build(
20
+ instance_methods: {
21
+ test1: {
22
+ body: -> { :test1 }
23
+ },
24
+ test2: {
25
+ body: -> { :test2 },
26
+ private: true
27
+ }
28
+ }
29
+ ).new
30
+ obj.test1.must_equal :test1
31
+ assert_raises NoMethodError do
32
+ obj.test2
33
+ end
34
+ obj.send(:test2).must_equal :test2
35
+ end
36
+
37
+ it 'should allow setting class methods' do
38
+ klass = build(
39
+ class_methods: {
40
+ test1: {
41
+ body: -> { :test1 }
42
+ },
43
+ test2: {
44
+ body: -> { :test2 },
45
+ private: true
46
+ }
47
+ }
48
+ )
49
+ klass.test1.must_equal :test1
50
+ assert_raises NoMethodError do
51
+ klass.test2
52
+ end
53
+ klass.send(:test2).must_equal :test2
54
+ end
55
+
56
+ it 'should allow setting super class' do
57
+ super_class = Class.new
58
+ build(super_class: super_class).superclass.must_equal super_class
59
+ end
60
+
61
+ it 'should allow setting super class by name' do
62
+ class AlkiTestClass; end
63
+ build(super_class: 'alki_test_class').superclass.must_equal AlkiTestClass
64
+ Object.send :remove_const, :AlkiTestClass
65
+ end
66
+
67
+ it 'should include module and extend module::ClassMethods if provided' do
68
+ m = Module.new do
69
+ module self::ClassMethods
70
+ def test1
71
+ :test1
72
+ end
73
+ end
74
+ def test2
75
+ :test2
76
+ end
77
+ end
78
+ klass = build(module: m)
79
+ klass.included_modules.must_include m
80
+ klass.test1.must_equal :test1
81
+ klass.new.test2.must_equal :test2
82
+ end
83
+
84
+ it 'should include modules' do
85
+ ms = [Module.new,Module.new]
86
+ klass = build(modules: ms)
87
+ ms.each do |m|
88
+ klass.included_modules.must_include m
89
+ end
90
+ end
91
+
92
+ it 'should extend class_modules' do
93
+ ms = [
94
+ Module.new { def test1; :test1; end },
95
+ Module.new { def test2; :test2; end },
96
+ ]
97
+ klass = build(class_modules: ms)
98
+ klass.test1.must_equal :test1
99
+ klass.test2.must_equal :test2
100
+ end
101
+
102
+ it 'should create basic #initialize using initialize_params' do
103
+ obj = build(initialize_params: [:a,:b]).new 1, 2
104
+ obj.instance_variable_get(:@a).must_equal 1
105
+ obj.instance_variable_get(:@b).must_equal 2
106
+ end
107
+
108
+ it 'should allow providing a class name' do
109
+ if defined?(AlkiTestClass)
110
+ Object.send :remove_const, :AlkiTestClass
111
+ end
112
+ build(class_name: "AlkiTestClass")
113
+ assert(defined?(AlkiTestClass),'Expected AlkiTestClass to be defined')
114
+ Object.send :remove_const, :AlkiTestClass
115
+ assert(!defined?(AlkiTestClass))
116
+ end
117
+
118
+ it 'should use name to create class name' do
119
+ if defined?(AlkiTest::TestClass)
120
+ Object.send :remove_const, :AlkiTest
121
+ end
122
+ build(prefix: '', name: "alki_test/test_class")
123
+ assert(defined?(AlkiTest::TestClass),'Expected AlkiTest::TestClass to be defined')
124
+ Object.send :remove_const, :AlkiTest
125
+ end
126
+
127
+ it 'should use prefix and name to create class name' do
128
+ if defined?(AlkiTest::TestClass)
129
+ Object.send :remove_const, :AlkiTest
130
+ end
131
+ build(prefix: 'alki_test', name: "test_class")
132
+ assert(defined?(AlkiTest::TestClass),'Expected AlkiTest::TestClass to be defined')
133
+ Object.send :remove_const, :AlkiTest
134
+ end
135
+
136
+ it 'should allow creating secondary classes' do
137
+ build(
138
+ secondary_classes: [
139
+ {
140
+ class_name: 'AlkiTestClass'
141
+ }
142
+ ]
143
+ )
144
+ assert(defined?(AlkiTestClass),'Expected AlkiTestClass to be defined')
145
+ Object.send :remove_const, :AlkiTestClass
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib',__FILE__)
2
+
3
+ require 'minitest/autorun'
4
+
5
+ class Minitest::Spec
6
+ def root
7
+ File.expand_path('../..',__FILE__)
8
+ end
9
+
10
+ def fixture_path(*path)
11
+ File.join(root,'test','fixtures',*path)
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ require_relative '../test_helper'
2
+ require 'alki/dsl/evaluator'
3
+
4
+ describe Alki::Dsl::Evaluator do
5
+
6
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alki-dsl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Matt Edlefsen
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-12-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: alki-support
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.9'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 5.9.1
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '5.9'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 5.9.1
47
+ description: Library for defining and using DSLs
48
+ email:
49
+ - matt.edlefsen@gmail.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - ".gitignore"
55
+ - Gemfile
56
+ - alki-dsl.gemspec
57
+ - lib/alki/class_builder.rb
58
+ - lib/alki/dsl.rb
59
+ - lib/alki/dsl/base.rb
60
+ - lib/alki/dsl/builder.rb
61
+ - lib/alki/dsl/class_helpers.rb
62
+ - lib/alki/dsl/evaluator.rb
63
+ - lib/alki/dsl/loader.rb
64
+ - lib/alki/dsl/registry.rb
65
+ - lib/alki/dsl/version.rb
66
+ - lib/alki/dsls/class.rb
67
+ - lib/alki/dsls/dsl.rb
68
+ - lib/alki/dsls/dsl_config.rb
69
+ - test/feature/config_test.rb
70
+ - test/fixtures/example/config/dsls.rb
71
+ - test/fixtures/example/lib/alki_test/dsls/number.rb
72
+ - test/fixtures/example/lib/alki_test/dsls/value.rb
73
+ - test/fixtures/example/numbers/three.rb
74
+ - test/integration/class_builder_test.rb
75
+ - test/test_helper.rb
76
+ - test/unit/evaluator_test.rb
77
+ homepage: https://github.com/medlefsen/alki-dsl
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.5.1
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Alki dsl library
101
+ test_files:
102
+ - test/feature/config_test.rb
103
+ - test/fixtures/example/config/dsls.rb
104
+ - test/fixtures/example/lib/alki_test/dsls/number.rb
105
+ - test/fixtures/example/lib/alki_test/dsls/value.rb
106
+ - test/fixtures/example/numbers/three.rb
107
+ - test/integration/class_builder_test.rb
108
+ - test/test_helper.rb
109
+ - test/unit/evaluator_test.rb