datacaster 2.0.2 → 3.0.0

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