datacaster 2.0.2 → 3.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +518 -268
  3. data/config/locales/en.yml +24 -0
  4. data/datacaster.gemspec +2 -0
  5. data/lib/datacaster/absent.rb +4 -0
  6. data/lib/datacaster/and_node.rb +3 -5
  7. data/lib/datacaster/and_with_error_aggregation_node.rb +5 -6
  8. data/lib/datacaster/array_schema.rb +18 -16
  9. data/lib/datacaster/base.rb +33 -44
  10. data/lib/datacaster/caster.rb +4 -8
  11. data/lib/datacaster/checker.rb +8 -10
  12. data/lib/datacaster/comparator.rb +9 -9
  13. data/lib/datacaster/config.rb +28 -0
  14. data/lib/datacaster/context_node.rb +43 -0
  15. data/lib/datacaster/context_nodes/errors_caster.rb +21 -0
  16. data/lib/datacaster/context_nodes/i18n.rb +20 -0
  17. data/lib/datacaster/context_nodes/i18n_keys_mapper.rb +27 -0
  18. data/lib/datacaster/context_nodes/structure_cleaner.rb +103 -0
  19. data/lib/datacaster/context_nodes/user_context.rb +20 -0
  20. data/lib/datacaster/definition_dsl.rb +37 -0
  21. data/lib/datacaster/hash_mapper.rb +13 -16
  22. data/lib/datacaster/hash_schema.rb +14 -15
  23. data/lib/datacaster/i18n_values/base.rb +87 -0
  24. data/lib/datacaster/i18n_values/key.rb +34 -0
  25. data/lib/datacaster/i18n_values/scope.rb +28 -0
  26. data/lib/datacaster/message_keys_merger.rb +8 -15
  27. data/lib/datacaster/or_node.rb +3 -4
  28. data/lib/datacaster/predefined.rb +119 -64
  29. data/lib/datacaster/result.rb +35 -14
  30. data/lib/datacaster/runtimes/base.rb +47 -0
  31. data/lib/datacaster/runtimes/i18n.rb +20 -0
  32. data/lib/datacaster/runtimes/structure_cleaner.rb +47 -0
  33. data/lib/datacaster/runtimes/user_context.rb +39 -0
  34. data/lib/datacaster/substitute_i18n.rb +48 -0
  35. data/lib/datacaster/then_node.rb +7 -8
  36. data/lib/datacaster/transformer.rb +4 -8
  37. data/lib/datacaster/trier.rb +9 -11
  38. data/lib/datacaster/validator.rb +8 -9
  39. data/lib/datacaster/version.rb +1 -1
  40. data/lib/datacaster.rb +15 -35
  41. metadata +57 -9
  42. data/lib/datacaster/definition_context.rb +0 -20
  43. data/lib/datacaster/terminator.rb +0 -98
@@ -2,13 +2,15 @@ require 'dry/monads'
2
2
 
3
3
  module Datacaster
4
4
  class Result
5
- attr_accessor :meta
6
5
  include Dry::Monads[:result]
7
6
 
8
- def initialize(valid, value_or_errors, meta: nil)
7
+ def initialize(valid, value_or_errors)
9
8
  @value_or_errors = value_or_errors
9
+ if !valid && !@value_or_errors.is_a?(Hash) && !@value_or_errors.is_a?(Array)
10
+ @value_or_errors = Array(@value_or_errors)
11
+ end
12
+
10
13
  @valid = !!valid
11
- @meta = meta || {}
12
14
  end
13
15
 
14
16
  def valid?
@@ -19,13 +21,19 @@ module Datacaster
19
21
  @valid ? @value_or_errors : nil
20
22
  end
21
23
 
22
- def errors
23
- unless @value_or_errors.is_a?(Hash) || @value_or_errors.is_a?(Array)
24
- @value_or_errors = Array(@value_or_errors)
25
- end
24
+ def value!
25
+ raise "Tried to unwrap value of error result: #{inspect}" unless valid?
26
+ value
27
+ end
28
+
29
+ def raw_errors
26
30
  @valid ? nil : @value_or_errors
27
31
  end
28
32
 
33
+ def errors
34
+ @errors ||= @valid ? nil : resolve_i18n(raw_errors)
35
+ end
36
+
29
37
  def inspect
30
38
  if @valid
31
39
  "#<Datacaster::ValidResult(#{@value_or_errors.inspect})>"
@@ -35,27 +43,40 @@ module Datacaster
35
43
  end
36
44
 
37
45
  def to_dry_result
38
- @valid ? Success(@value_or_errors) : Failure(@value_or_errors)
46
+ @valid ? Success(@value_or_errors) : Failure(errors)
47
+ end
48
+
49
+ private
50
+
51
+ def resolve_i18n(o)
52
+ case o
53
+ when Array
54
+ o.map { |x| resolve_i18n(x) }
55
+ when Hash
56
+ o.transform_values { |x| resolve_i18n(x) }
57
+ when I18nValues::Base
58
+ o.resolve
59
+ else
60
+ o
61
+ end
39
62
  end
40
63
  end
41
64
 
42
- def self.ValidResult(object, meta: nil)
65
+ def self.ValidResult(object)
43
66
  if object.is_a?(Result)
44
67
  raise "Can't create valid result from error #{object.inspect}" unless object.valid?
45
- object.meta = meta if meta
46
68
  object
47
69
  else
48
- Result.new(true, object, meta: meta)
70
+ Result.new(true, object)
49
71
  end
50
72
  end
51
73
 
52
- def self.ErrorResult(object, meta: nil)
74
+ def self.ErrorResult(object)
53
75
  if object.is_a?(Result)
54
76
  raise "Can't create error result from valid #{object.inspect}" if object.valid?
55
- object.meta = meta if meta
56
77
  object
57
78
  else
58
- Result.new(false, object, meta: meta)
79
+ Result.new(false, object)
59
80
  end
60
81
  end
61
82
  end
@@ -0,0 +1,47 @@
1
+ module Datacaster
2
+ module Runtimes
3
+ class Base
4
+ def self.call(r, proc, *args)
5
+ r.instance_exec(*args, &proc)
6
+ end
7
+
8
+ def self.send_to_parent(r, m, *args, &block)
9
+ parent = r.instance_variable_get(:@parent)
10
+ not_found!(m) if parent.nil?
11
+ call(parent, -> { public_send(m, *args, &block) })
12
+ end
13
+
14
+ def self.not_found!(m)
15
+ raise NoMethodError.new("Method #{m.inspect} is not available in current runtime context")
16
+ end
17
+
18
+ def initialize(parent = nil)
19
+ @parent = parent
20
+ end
21
+
22
+ def method_missing(m, *args, &block)
23
+ self.class.send_to_parent(self, m, *args, &block)
24
+ end
25
+
26
+ def respond_to_missing?(m, include_private = false)
27
+ !@parent.nil? && @parent.respond_to?(m, include_private)
28
+ end
29
+
30
+ def inspect
31
+ "#<#{self.class.name} parent: #{@parent.inspect}>"
32
+ end
33
+
34
+ def to_s
35
+ inspect
36
+ end
37
+
38
+ def Success(v)
39
+ Datacaster.ValidResult(v)
40
+ end
41
+
42
+ def Failure(v)
43
+ Datacaster.ErrorResult(v)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,20 @@
1
+ module Datacaster
2
+ module Runtimes
3
+ class I18n < Base
4
+ attr_reader :args
5
+
6
+ def initialize(*)
7
+ super
8
+ @args = {}
9
+ end
10
+
11
+ def i18n_var!(name, value)
12
+ @args[name] = value
13
+ end
14
+
15
+ def i18n_vars!(map)
16
+ @args.merge!(map)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ module Datacaster
2
+ module Runtimes
3
+ class StructureCleaner < Base
4
+ attr_reader :checked_schema
5
+
6
+ def initialize(*)
7
+ super
8
+ @checked_schema = {}
9
+ @should_check_stack = [false]
10
+ @pointer_stack = [@checked_schema]
11
+ end
12
+
13
+ # Array checked schema are the same as hash one, where
14
+ # instead of keys there are array indicies
15
+ def checked_key!(key)
16
+ if @ignore
17
+ return yield if block_given?
18
+ return
19
+ end
20
+
21
+ @pointer_stack.last[key] ||= {}
22
+ @pointer_stack.push(@pointer_stack.last[key])
23
+ @should_check_stack.push(false)
24
+ result = yield if block_given?
25
+ was_checked = @should_check_stack.pop
26
+ @pointer_stack.pop
27
+ @pointer_stack.last[key] = true unless was_checked
28
+ result
29
+ end
30
+
31
+ def will_check!
32
+ @should_check_stack[-1] = true
33
+ end
34
+
35
+ def ignore_checks!(&block)
36
+ @ignore = true
37
+ result = yield
38
+ @ignore = false
39
+ result
40
+ end
41
+
42
+ def unchecked?
43
+ @should_check_stack == [false]
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,39 @@
1
+ require 'ostruct'
2
+
3
+ module Datacaster
4
+ module Runtimes
5
+ class UserContext < Base
6
+ class ContextStruct
7
+ def initialize(context, node)
8
+ @context = context
9
+ @node = node
10
+ end
11
+
12
+ def method_missing(m, *args)
13
+ if !args.empty? || block_given?
14
+ return super
15
+ end
16
+
17
+ if @context.key?(m)
18
+ return @context[m]
19
+ end
20
+
21
+ begin
22
+ @node.class.send_to_parent(@node, :context).public_send(m)
23
+ rescue NoMethodError
24
+ raise NoMethodError.new("Key #{m.inspect} is not found in the context")
25
+ end
26
+ end
27
+ end
28
+
29
+ def initialize(parent, user_context)
30
+ super(parent)
31
+ @context_struct = ContextStruct.new(user_context, self)
32
+ end
33
+
34
+ def context
35
+ @context_struct
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,48 @@
1
+ require 'yaml'
2
+
3
+ module Datacaster
4
+ module SubstituteI18n
5
+ @load_path = []
6
+
7
+ def self.exists?(key)
8
+ !fetch(key).nil?
9
+ end
10
+
11
+ def self.fetch(key)
12
+ keys = [locale] + key.split('.')
13
+
14
+ @translations.each do |hash|
15
+ result = hash.dig(*keys)
16
+ return result unless result.nil?
17
+ end
18
+ nil
19
+ end
20
+
21
+ def self.load_path
22
+ @load_path
23
+ end
24
+
25
+ def self.load_path=(array)
26
+ @load_path = array
27
+ @translations = array.map { |x| YAML.load_file(x) }
28
+ end
29
+
30
+ def self.locale
31
+ 'en'
32
+ end
33
+
34
+ def self.locale=(*)
35
+ raise NotImplementedError.new("Setting locale is not supported, use ruby-i18n instead of datacaster's built-in")
36
+ end
37
+
38
+ def self.t(key, **args)
39
+ string = fetch(key)
40
+ return "Translation missing #{key}" unless string
41
+
42
+ args.each do |from, to|
43
+ string = string.gsub("%{#{from}}", to.to_s)
44
+ end
45
+ string
46
+ end
47
+ end
48
+ end
@@ -1,29 +1,28 @@
1
1
  module Datacaster
2
2
  class ThenNode < Base
3
- def initialize(left, then_caster)
3
+ def initialize(left, then_caster, else_caster = nil)
4
4
  @left = left
5
5
  @then = then_caster
6
+ @else = else_caster
6
7
  end
7
8
 
8
9
  def else(else_caster)
9
10
  raise ArgumentError.new("Datacaster: double else clause is not permitted") if @else
10
11
 
11
- @else = else_caster
12
- self
12
+ self.class.new(@left, @then, else_caster)
13
13
  end
14
14
 
15
- def cast(object)
15
+ def cast(object, runtime:)
16
16
  unless @else
17
17
  raise ArgumentError.new('Datacaster: use "a & b" instead of "a.then(b)" when there is no else-clause')
18
18
  end
19
19
 
20
- object = super(object)
21
- left_result = @left.(object)
20
+ left_result = @left.with_runtime(runtime).(object)
22
21
 
23
22
  if left_result.valid?
24
- @then.(left_result)
23
+ @then.with_runtime(runtime).(left_result.value)
25
24
  else
26
- @else.(object)
25
+ @else.with_runtime(runtime).(object)
27
26
  end
28
27
  end
29
28
 
@@ -1,21 +1,17 @@
1
1
  module Datacaster
2
2
  class Transformer < Base
3
- def initialize(name, &block)
3
+ def initialize(&block)
4
4
  raise "Expected block" unless block_given?
5
5
 
6
- @name = name
7
6
  @transform = block
8
7
  end
9
8
 
10
- def cast(object)
11
- intermediary_result = super(object)
12
- object = intermediary_result.value
13
-
14
- Datacaster.ValidResult(@transform.(object))
9
+ def cast(object, runtime:)
10
+ Datacaster.ValidResult(Runtimes::Base.(runtime, @transform, object))
15
11
  end
16
12
 
17
13
  def inspect
18
- "#<Datacaster::#{@name}Transformer>"
14
+ "#<Datacaster::Transformer>"
19
15
  end
20
16
  end
21
17
  end
@@ -1,27 +1,25 @@
1
1
  module Datacaster
2
2
  class Trier < Base
3
- def initialize(name, error, catched_exception, &block)
3
+ def initialize(catched_exception, error_key = nil, &block)
4
4
  raise "Expected block" unless block_given?
5
5
 
6
- @name = name
7
- @error = error
8
6
  @catched_exception = Array(catched_exception)
9
- @transform = block
10
- end
7
+ @try = block
11
8
 
12
- def cast(object)
13
- intermediary_result = super(object)
14
- object = intermediary_result.value
9
+ @error_keys = ['.try', 'datacaster.errors.try']
10
+ @error_keys.unshift(error_key) if error_key
11
+ end
15
12
 
13
+ def cast(object, runtime:)
16
14
  begin
17
- Datacaster.ValidResult(@transform.(object))
15
+ Datacaster.ValidResult(Runtimes::Base.(runtime, @try, object))
18
16
  rescue *@catched_exception
19
- Datacaster.ErrorResult([@error])
17
+ Datacaster.ErrorResult(I18nValues::Key.new(@error_keys, value: object))
20
18
  end
21
19
  end
22
20
 
23
21
  def inspect
24
- "#<Datacaster::#{@name}Trier>"
22
+ "#<Datacaster::Trier>"
25
23
  end
26
24
  end
27
25
  end
@@ -1,5 +1,3 @@
1
- require 'active_model'
2
-
3
1
  module Datacaster
4
2
  class Validator < Base
5
3
  @@validations = {}
@@ -21,21 +19,22 @@ module Datacaster
21
19
  end.new
22
20
  end
23
21
 
24
- def initialize(validations, name)
25
- @name = name
22
+ def initialize(validations)
23
+ require 'active_model'
24
+
25
+ if Config.i18n_module == SubstituteI18n
26
+ raise NotImplementedError, "Using ActiveModel validations requires ruby-i18n or another i18n gem instead of datacaster's built-in", caller
27
+ end
26
28
  @validator = self.class.create_active_model(validations)
27
29
  end
28
30
 
29
- def cast(object)
30
- intermediary_result = super(object)
31
- object = intermediary_result.value
32
-
31
+ def cast(object, runtime:)
33
32
  @validator.value = object
34
33
  @validator.valid? ? Datacaster.ValidResult(object) : Datacaster.ErrorResult(@validator.errors[:value])
35
34
  end
36
35
 
37
36
  def inspect
38
- "#<Datacaster::#{@name}Validator>"
37
+ "#<Datacaster::Validator>"
39
38
  end
40
39
  end
41
40
  end
@@ -1,3 +1,3 @@
1
1
  module Datacaster
2
- VERSION = "2.0.2"
2
+ VERSION = "3.0.0"
3
3
  end
data/lib/datacaster.rb CHANGED
@@ -1,41 +1,23 @@
1
+ require 'zeitwerk'
2
+ loader = Zeitwerk::Loader.for_gem
3
+ loader.inflector.inflect('definition_dsl' => 'DefinitionDSL')
4
+ loader.setup
5
+
1
6
  require_relative 'datacaster/result'
2
- require_relative 'datacaster/version'
3
-
4
- require_relative 'datacaster/absent'
5
- require_relative 'datacaster/base'
6
- require_relative 'datacaster/predefined'
7
- require_relative 'datacaster/definition_context'
8
- require_relative 'datacaster/terminator'
9
- require_relative 'datacaster/config'
10
-
11
- require_relative 'datacaster/array_schema'
12
- require_relative 'datacaster/caster'
13
- require_relative 'datacaster/checker'
14
- require_relative 'datacaster/comparator'
15
- require_relative 'datacaster/hash_mapper'
16
- require_relative 'datacaster/hash_schema'
17
- require_relative 'datacaster/message_keys_merger'
18
- require_relative 'datacaster/transformer'
19
- require_relative 'datacaster/trier'
20
-
21
- require_relative 'datacaster/and_node'
22
- require_relative 'datacaster/and_with_error_aggregation_node'
23
- require_relative 'datacaster/or_node'
24
- require_relative 'datacaster/then_node'
25
7
 
26
8
  module Datacaster
27
9
  extend self
28
10
 
29
- def schema(&block)
30
- build_schema(Terminator::Raising.instance, &block)
11
+ def schema(i18n_scope: nil, &block)
12
+ ContextNodes::StructureCleaner.new(build_schema(i18n_scope: i18n_scope, &block), :fail)
31
13
  end
32
14
 
33
- def choosy_schema(&block)
34
- build_schema(Terminator::Sweeping.instance, &block)
15
+ def choosy_schema(i18n_scope: nil, &block)
16
+ ContextNodes::StructureCleaner.new(build_schema(i18n_scope: i18n_scope, &block), :remove)
35
17
  end
36
18
 
37
- def partial_schema(&block)
38
- build_schema(nil, &block)
19
+ def partial_schema(i18n_scope: nil, &block)
20
+ ContextNodes::StructureCleaner.new(build_schema(i18n_scope: i18n_scope, &block), :pass)
39
21
  end
40
22
 
41
23
  def absent
@@ -44,19 +26,17 @@ module Datacaster
44
26
 
45
27
  private
46
28
 
47
- def build_schema(terminator, &block)
29
+ def build_schema(i18n_scope: nil, &block)
48
30
  raise "Expected block" unless block
49
31
 
50
- definition_context = DefinitionContext.new
51
-
52
- datacaster = definition_context.instance_exec(&block)
32
+ datacaster = DefinitionDSL.eval(&block)
53
33
 
54
34
  unless datacaster.is_a?(Base)
55
35
  raise "Datacaster instance should be returned from a block (e.g. result of 'hash_schema(...)' call)"
56
36
  end
57
37
 
58
- datacaster = (datacaster & terminator) if terminator
59
- datacaster.set_definition_context(definition_context)
38
+ datacaster = datacaster.i18n_scope(i18n_scope) if i18n_scope
39
+
60
40
  datacaster
61
41
  end
62
42
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datacaster
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eugene Zolotarev
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-02 00:00:00.000000000 Z
11
+ date: 2023-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: i18n
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.14'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.14'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: dry-monads
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -72,7 +86,27 @@ dependencies:
72
86
  - - "<"
73
87
  - !ruby/object:Gem::Version
74
88
  version: '1.4'
75
- description:
89
+ - !ruby/object:Gem::Dependency
90
+ name: zeitwerk
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '2'
96
+ - - "<"
97
+ - !ruby/object:Gem::Version
98
+ version: '3'
99
+ type: :runtime
100
+ prerelease: false
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '2'
106
+ - - "<"
107
+ - !ruby/object:Gem::Version
108
+ version: '3'
109
+ description:
76
110
  email:
77
111
  - eugzol@gmail.com
78
112
  executables: []
@@ -90,6 +124,7 @@ files:
90
124
  - Rakefile
91
125
  - bin/console
92
126
  - bin/setup
127
+ - config/locales/en.yml
93
128
  - datacaster.gemspec
94
129
  - lib/datacaster.rb
95
130
  - lib/datacaster/absent.rb
@@ -101,14 +136,27 @@ files:
101
136
  - lib/datacaster/checker.rb
102
137
  - lib/datacaster/comparator.rb
103
138
  - lib/datacaster/config.rb
104
- - lib/datacaster/definition_context.rb
139
+ - lib/datacaster/context_node.rb
140
+ - lib/datacaster/context_nodes/errors_caster.rb
141
+ - lib/datacaster/context_nodes/i18n.rb
142
+ - lib/datacaster/context_nodes/i18n_keys_mapper.rb
143
+ - lib/datacaster/context_nodes/structure_cleaner.rb
144
+ - lib/datacaster/context_nodes/user_context.rb
145
+ - lib/datacaster/definition_dsl.rb
105
146
  - lib/datacaster/hash_mapper.rb
106
147
  - lib/datacaster/hash_schema.rb
148
+ - lib/datacaster/i18n_values/base.rb
149
+ - lib/datacaster/i18n_values/key.rb
150
+ - lib/datacaster/i18n_values/scope.rb
107
151
  - lib/datacaster/message_keys_merger.rb
108
152
  - lib/datacaster/or_node.rb
109
153
  - lib/datacaster/predefined.rb
110
154
  - lib/datacaster/result.rb
111
- - lib/datacaster/terminator.rb
155
+ - lib/datacaster/runtimes/base.rb
156
+ - lib/datacaster/runtimes/i18n.rb
157
+ - lib/datacaster/runtimes/structure_cleaner.rb
158
+ - lib/datacaster/runtimes/user_context.rb
159
+ - lib/datacaster/substitute_i18n.rb
112
160
  - lib/datacaster/then_node.rb
113
161
  - lib/datacaster/transformer.rb
114
162
  - lib/datacaster/trier.rb
@@ -119,7 +167,7 @@ licenses:
119
167
  - MIT
120
168
  metadata:
121
169
  source_code_uri: https://github.com/EugZol/datacaster
122
- post_install_message:
170
+ post_install_message:
123
171
  rdoc_options: []
124
172
  require_paths:
125
173
  - lib
@@ -134,8 +182,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
182
  - !ruby/object:Gem::Version
135
183
  version: '0'
136
184
  requirements: []
137
- rubygems_version: 3.4.1
138
- signing_key:
185
+ rubygems_version: 3.4.6
186
+ signing_key:
139
187
  specification_version: 4
140
188
  summary: Run-time type checker and transformer for Ruby
141
189
  test_files: []
@@ -1,20 +0,0 @@
1
- require 'bigdecimal'
2
- require 'date'
3
-
4
- module Datacaster
5
- class DefinitionContext
6
- include Datacaster::Predefined
7
- include Dry::Monads[:result]
8
-
9
- attr_accessor :context
10
-
11
- def m(_definition)
12
- raise "not implemented"
13
- end
14
-
15
- def method_missing(m, *args)
16
- arg_string = args.empty? ? "" : "(#{args.map(&:inspect).join(', ')})"
17
- raise "Datacaster: unknown definition '#{m}#{arg_string}'"
18
- end
19
- end
20
- end