sinclair 1.10.0 → 1.12.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/README.md +444 -324
  4. data/config/check_specs.yml +5 -5
  5. data/config/yardstick.yml +4 -0
  6. data/lib/sinclair/matchers/add_class_method.rb +0 -1
  7. data/lib/sinclair/method_builder/base.rb +32 -2
  8. data/lib/sinclair/method_builder/block_method_builder.rb +1 -12
  9. data/lib/sinclair/method_builder/call_method_builder.rb +10 -27
  10. data/lib/sinclair/method_builder/string_method_builder.rb +2 -29
  11. data/lib/sinclair/method_builder.rb +4 -20
  12. data/lib/sinclair/method_definition/block_definition.rb +2 -0
  13. data/lib/sinclair/method_definition/call_definition.rb +15 -18
  14. data/lib/sinclair/method_definition/parameter_builder.rb +89 -0
  15. data/lib/sinclair/method_definition/parameter_helper.rb +124 -0
  16. data/lib/sinclair/method_definition/string_definition.rb +26 -2
  17. data/lib/sinclair/method_definition.rb +42 -3
  18. data/lib/sinclair/method_definitions.rb +20 -22
  19. data/lib/sinclair/version.rb +1 -1
  20. data/lib/sinclair.rb +149 -62
  21. data/spec/integration/readme/sinclair/types_of_definition_spec.rb +61 -0
  22. data/spec/integration/yard/sinclair/add_class_method_spec.rb +51 -0
  23. data/spec/integration/yard/sinclair/add_method_spec.rb +60 -0
  24. data/spec/integration/yard/sinclair/eval_and_add_method_spec.rb +26 -0
  25. data/spec/integration/yard/sinclair_spec.rb +0 -83
  26. data/spec/lib/sinclair/method_builder/base_spec.rb +15 -0
  27. data/spec/lib/sinclair/method_builder/string_method_builder_spec.rb +24 -1
  28. data/spec/lib/sinclair/method_definition/call_definition_spec.rb +14 -17
  29. data/spec/lib/sinclair/method_definition/parameter_builder_spec.rb +81 -0
  30. data/spec/lib/sinclair/method_definition/string_definition_spec.rb +60 -29
  31. data/spec/lib/sinclair/method_definition_spec.rb +77 -2
  32. data/spec/lib/sinclair/method_definitions_spec.rb +15 -16
  33. data/spec/lib/sinclair_spec.rb +6 -160
  34. data/spec/support/models/dummy_builder.rb +5 -1
  35. data/spec/support/models/dummy_class_builder.rb +4 -0
  36. data/spec/support/models/person.rb +1 -1
  37. data/spec/support/shared_examples/sinclair.rb +118 -0
  38. metadata +11 -2
@@ -1,10 +1,10 @@
1
1
  ignore:
2
2
  - lib/sinclair/comparable/class_methods.rb
3
- - lib/sinclair/matchers/add_method_to.rb
4
- - lib/sinclair/matchers/add_method.rb
5
- - lib/sinclair/version.rb
6
- - lib/sinclair/method_builder/base.rb
7
3
  - lib/sinclair/exception.rb
8
- - lib/sinclair/matchers/method_to.rb
4
+ - lib/sinclair/matchers/add_method.rb
5
+ - lib/sinclair/matchers/add_method_to.rb
9
6
  - lib/sinclair/matchers/base.rb
10
7
  - lib/sinclair/matchers/change_method_on.rb
8
+ - lib/sinclair/matchers/method_to.rb
9
+ - lib/sinclair/method_definition/parameter_helper.rb
10
+ - lib/sinclair/version.rb
data/config/yardstick.yml CHANGED
@@ -16,6 +16,8 @@ rules:
16
16
  ExampleTag:
17
17
  enabled: true
18
18
  exclude:
19
+ - Sinclair#add_method
20
+ - Sinclair#add_class_method
19
21
  - Sinclair::Configurable#config
20
22
  - Sinclair::Configurable#reset_config
21
23
  - Sinclair::Configurable#configure
@@ -58,6 +60,8 @@ rules:
58
60
  - Sinclair::MethodDefinition#initialize
59
61
  - Sinclair::MethodDefinition::CallDefinition#initialize
60
62
  - Sinclair::MethodDefinition::BlockDefinition#initialize
63
+ - Sinclair::MethodDefinition::ParameterBuilder#initialize
64
+ - Sinclair::MethodDefinition::ParameterHelper#initialize
61
65
  - Sinclair::MethodDefinition::StringDefinition#initialize
62
66
  - Sinclair::Options#initialize
63
67
  - Sinclair::Options::Builder#initialize
@@ -7,7 +7,6 @@ class Sinclair
7
7
  #
8
8
  # AddClassMethod is able to build an instance of {Sinclair::Matchers::AddClassMethodTo}
9
9
  class AddClassMethod < AddMethod
10
- # @method #to(target = nil)
11
10
  # @example Checking if a class had a class method added
12
11
  # RSpec.configure do |config|
13
12
  # config.include Sinclair::Matchers
@@ -7,6 +7,21 @@ class Sinclair
7
7
  #
8
8
  # Base class responsible for building methods
9
9
  class Base
10
+ # Instantiate the class and build the method
11
+ #
12
+ # @param klass [Class] class to receive the method
13
+ # @param definition [MethodDefinition] method defined
14
+ # @param type [Symbol] type of method to be build
15
+ # - +:instance+ instance methods
16
+ # - +:class+ class methods
17
+ #
18
+ # @see #build
19
+ #
20
+ # @return [Symbol] name of the method built
21
+ def self.build(klass, definition, type:)
22
+ new(klass, definition, type: type).build
23
+ end
24
+
10
25
  # @param klass [Class] class to receive the method
11
26
  # @param definition [MethodDefinition] method defined
12
27
  # @param type [Symbol] type of method to be build
@@ -22,9 +37,9 @@ class Sinclair
22
37
  #
23
38
  # @return [Symbol] name of the method built
24
39
  #
25
- # @raise NotImplementedYet
40
+ # @raise NotImplementedError
26
41
  def build
27
- raise 'Not implemented yet. this should be imlemented in subclasses'
42
+ raise NotImplementedError, 'Not implemented yet. this should be imlemented in subclasses'
28
43
  end
29
44
 
30
45
  private
@@ -62,6 +77,21 @@ class Sinclair
62
77
  def instance?
63
78
  type == INSTANCE_METHOD
64
79
  end
80
+
81
+ # Returns the klass where the proc block will be evaluated
82
+ #
83
+ # For instance type, the class itself is returned
84
+ #
85
+ # For adding class methods, the superclass is returned
86
+ #
87
+ # @return [Class]
88
+ def evaluating_class
89
+ return klass if instance?
90
+
91
+ class << klass
92
+ return self
93
+ end
94
+ end
65
95
  end
66
96
  end
67
97
  end
@@ -14,21 +14,10 @@ class Sinclair
14
14
  #
15
15
  # @return (see Base#build)
16
16
  def build
17
- klass.send(method_definition, name, method_block)
17
+ evaluating_class.define_method(name, method_block)
18
18
  end
19
19
 
20
- private
21
-
22
20
  delegate :name, :method_block, to: :definition
23
-
24
- # @private
25
- #
26
- # name of the method used to define a new method on class
27
- #
28
- # @return [Symbol]
29
- def method_definition
30
- instance? ? :define_method : :define_singleton_method
31
- end
32
21
  end
33
22
  end
34
23
  end
@@ -9,41 +9,24 @@ class Sinclair
9
9
  class CallMethodBuilder < Base
10
10
  # Builds the method
11
11
  #
12
+ # The build uses +module_eval+ over a class
13
+ #
14
+ # The code is ran either on the class itself or in
15
+ # a block that allow creation of class methods
16
+ #
12
17
  # @return [NilClass]
13
18
  def build
14
- klass.module_eval(code_line, __FILE__, __LINE__ + 1)
19
+ evaluating_class.module_eval(&code_block)
15
20
  end
16
21
 
17
- private
18
-
22
+ delegate :code_block, to: :definition
23
+ # @method code_block
19
24
  # @api private
20
25
  # @private
21
26
  #
22
- # String to be evaluated when building the method
23
- #
24
- # This can be {code_string} or {class_code_string}
25
- # @return (see MethodDefinition::CallDefinition#code_string)
26
- def code_line
27
- instance? ? code_string : class_code_string
28
- end
29
-
30
- delegate :code_string, :class_code_string, to: :definition
31
-
32
- # @method code_string
33
- # @private
34
- # @api private
35
- #
36
- # Delegated from {MethodDefinition::CallDefinition}
37
- #
38
- # @see MethodDefinition::CallDefinition#code_string
39
- # @return [String]
40
-
41
- # @method class_code_string
42
- # @private
43
- # @api private
27
+ # Code block to be evaluated by the class
44
28
  #
45
- # @see MethodDefinition::CallDefinition#class_code_string
46
- # @return [String]
29
+ # @return [Proc]
47
30
  end
48
31
  end
49
32
  end
@@ -14,37 +14,10 @@ class Sinclair
14
14
  #
15
15
  # @return (see Base#build)
16
16
  def build
17
- klass.module_eval(code_definition, __FILE__, __LINE__ + 1)
17
+ evaluating_class.module_eval(code_definition, __FILE__, __LINE__ + 1)
18
18
  end
19
19
 
20
- private
21
-
22
- # @private
23
- #
24
- # string used for method name definition
25
- #
26
- # the string changes depending if it is
27
- # a class or instance method
28
- #
29
- # @return [String]
30
- def definition_name
31
- instance? ? name : "self.#{name}"
32
- end
33
-
34
- # @private
35
- #
36
- # string with the code to be defined
37
- #
38
- # @return [String]
39
- def code_definition
40
- <<-CODE
41
- def #{definition_name}
42
- #{code_line}
43
- end
44
- CODE
45
- end
46
-
47
- delegate :code_line, :name, to: :definition
20
+ delegate :code_definition, to: :definition
48
21
  end
49
22
  end
50
23
  end
@@ -7,9 +7,9 @@ class Sinclair
7
7
  # Class responsible for building methods
8
8
  class MethodBuilder
9
9
  autoload :Base, 'sinclair/method_builder/base'
10
- autoload :StringMethodBuilder, 'sinclair/method_builder/string_method_builder'
11
10
  autoload :BlockMethodBuilder, 'sinclair/method_builder/block_method_builder'
12
11
  autoload :CallMethodBuilder, 'sinclair/method_builder/call_method_builder'
12
+ autoload :StringMethodBuilder, 'sinclair/method_builder/string_method_builder'
13
13
 
14
14
  CLASS_METHOD = :class
15
15
  INSTANCE_METHOD = :instance
@@ -24,11 +24,13 @@ class Sinclair
24
24
  # @param definitions [MethodDefinitions] all methods
25
25
  # definitions to be built
26
26
  # @param type [Symbol] type of method to be built
27
+ # - {CLASS_METHOD} : A class method will be built
28
+ # - {INSTANCE_METHOD} : An instance method will be built
27
29
  #
28
30
  # @return [MethodDefinitions]
29
31
  def build_methods(definitions, type)
30
32
  definitions.each do |definition|
31
- build_from_definition(definition, type)
33
+ definition.build(klass, type)
32
34
  end
33
35
  end
34
36
 
@@ -42,23 +44,5 @@ class Sinclair
42
44
  # class to receive the method
43
45
  #
44
46
  # @return [Class]
45
-
46
- # @private
47
- #
48
- # Build one method from definition
49
- #
50
- # @param definition [MethodDefinition] the method definition
51
- # @param type [Symbol] type of method to be built
52
- #
53
- # @return (see Base#build)
54
- def build_from_definition(definition, type)
55
- if definition.string?
56
- StringMethodBuilder.new(klass, definition, type: type).build
57
- elsif definition.block?
58
- BlockMethodBuilder.new(klass, definition, type: type).build
59
- else
60
- CallMethodBuilder.new(klass, definition, type: type).build
61
- end
62
- end
63
47
  end
64
48
  end
@@ -7,6 +7,8 @@ class Sinclair
7
7
  #
8
8
  # Define a method from block
9
9
  class BlockDefinition < MethodDefinition
10
+ build_with MethodBuilder::BlockMethodBuilder
11
+
10
12
  # @param name [String,Symbol] name of the method
11
13
  # @param block [Proc] block with code to be added as method
12
14
  # @param options [Hash] Options of construction
@@ -7,6 +7,8 @@ class Sinclair
7
7
  #
8
8
  # Define a call of method to e done within the class
9
9
  class CallDefinition < MethodDefinition
10
+ build_with MethodBuilder::CallMethodBuilder
11
+
10
12
  # @param method_name [Symbol] method to be called
11
13
  # @param arguments [Array<Symbol,String>] parameters to be passed as
12
14
  # arguments to the call
@@ -18,35 +20,30 @@ class Sinclair
18
20
  default_value :block?, false
19
21
  default_value :string?, false
20
22
 
21
- # String to be executed within the class
22
- # @return [String]
23
- def code_string
24
- "#{name} :#{arguments.join(', :')}"
25
- end
26
-
27
- # String to be executed within the class running code to change the class itself
23
+ # Block to be evaluated by the class when adding methods
28
24
  #
29
- # @see code_string
30
- # @return [String]
31
- def class_code_string
32
- <<-CODE
33
- class << self
34
- #{code_string}
35
- end
36
- CODE
25
+ # The block will be a call from +method_name+ passing +arguments+
26
+ # as arguments
27
+ # @return [Proc]
28
+ def code_block
29
+ method_name = name
30
+ args = arguments
31
+
32
+ proc do
33
+ send(method_name, *args)
34
+ end
37
35
  end
38
36
 
39
37
  private
40
38
 
41
39
  attr_reader :arguments
42
-
43
40
  # @method arguments
44
41
  # @api private
45
42
  # @private
46
43
  #
47
- # parameters to be passed as arguments to the call
44
+ # Arguments to be passed when calling the method inside the block
48
45
  #
49
- # @return [Array<Symbol,String>]
46
+ # @return [Array<Object>]
50
47
  end
51
48
  end
52
49
  end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ class MethodDefinition
5
+ # @api private
6
+ # @author darthjee
7
+ #
8
+ # Builder a string of parameters
9
+ #
10
+ # This is used when creating the string for a method defined
11
+ # using a string definition
12
+ #
13
+ # @see StringDefinition
14
+ class ParameterBuilder
15
+ # Builds a string representing method parameters
16
+ #
17
+ # @overload from(parameters, named_parameters)
18
+ # @param parameters [Array<Object>] List of parameters.
19
+ # @param named_parameters [Array<Object>] List of named parameters
20
+ #
21
+ # The list of +parameters+/+named_parameters+ is formed by an
22
+ # array of +Symbol+ representing required parameters
23
+ # and +Hash+ representing parameters with default values
24
+ #
25
+ # @return [String]
26
+ def self.from(*args)
27
+ new(*args).to_s
28
+ end
29
+
30
+ private_class_method :new
31
+
32
+ # @param parameters [Array<Object>] List of parameters.
33
+ # @param named_parameters [Array<Object>] List of named parameters
34
+ def initialize(parameters, named_parameters)
35
+ @parameters = parameters
36
+ @named_parameters = named_parameters
37
+ end
38
+
39
+ # Returns the parameters string
40
+ #
41
+ # @return [String]
42
+ def to_s
43
+ return '' if empty_parameters?
44
+
45
+ "(#{parameters_string})"
46
+ end
47
+
48
+ private
49
+
50
+ attr_reader :parameters, :named_parameters
51
+
52
+ # @!method parameters
53
+ # @api private
54
+ # @private
55
+ #
56
+ # List of parameters
57
+ #
58
+ # @return [Array<Object>]
59
+
60
+ # @!method named_parameters
61
+ # @api private
62
+ # @private
63
+ #
64
+ # List of named parameters
65
+ #
66
+ # @return [Array<Object>]
67
+
68
+ # @private
69
+ # Flag if any kind of parameters have not been provided
70
+ #
71
+ # @return [TrueClass,FalseClass]
72
+ def empty_parameters?
73
+ !parameters.present? && !named_parameters.present?
74
+ end
75
+
76
+ # String of parameters witout ()
77
+ #
78
+ # This will join all individual parameters strings by +,+
79
+ #
80
+ # @return [String]
81
+ def parameters_string
82
+ (
83
+ ParameterHelper.parameters_from(parameters) +
84
+ ParameterHelper.parameters_from(named_parameters, named: true)
85
+ ).join(', ')
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ class MethodDefinition
5
+ # @api private
6
+ # @author darthjee
7
+ #
8
+ # Helper containing helepr methods for remapping parameters
9
+ #
10
+ # @see ParameterBuilder
11
+ class ParameterHelper
12
+ # Returns a list of strings of parameters
13
+ #
14
+ # @overload parameters_from(parameters_list, named: false)
15
+ # @param parameters_list [Array<Object>] list of parameters and defaults
16
+ # @param named [TrueClass,FalseClass] Flag informing if the parameters are
17
+ # named parameters
18
+ #
19
+ # @return [String]
20
+ def self.parameters_from(*args, **opts)
21
+ new(*args, **opts).strings
22
+ end
23
+
24
+ private_class_method :new
25
+
26
+ # @param parameters_list [Array<Object>] list of parameters and defaults
27
+ # @param named [TrueClass,FalseClass] Flag informing if the parameters are
28
+ # named parameters
29
+ def initialize(parameters_list, named: false)
30
+ @parameters_list = parameters_list
31
+ @named = named
32
+ end
33
+
34
+ # All parameters converted into strings
35
+ #
36
+ # The strings are ready to be pushed into a method definition
37
+ # and joined by +,+
38
+ #
39
+ # @return [Array<String>]
40
+ def strings
41
+ return [] unless parameters_list
42
+
43
+ parameters_strings + defaults_strings
44
+ end
45
+
46
+ private
47
+
48
+ attr_reader :parameters_list, :named
49
+ alias named? named
50
+
51
+ # @!method parameters_list
52
+ # @api private
53
+ # @private
54
+ #
55
+ # List of parameters and parameters with defaults
56
+ #
57
+ # @return [Array<Object>]
58
+
59
+ # @!method named
60
+ # @api private
61
+ # @private
62
+ #
63
+ # Flag informing if the parameters are named parameters
64
+ #
65
+ # @return [TrueClass,FalseClass]
66
+
67
+ # @!method named?
68
+ # @api private
69
+ # @private
70
+ #
71
+ # Flag informing if the parameters are named parameters
72
+ #
73
+ # @return [TrueClass,FalseClass]
74
+
75
+ # Parameters without defaults
76
+ #
77
+ # These are filtered out from {#parameters_list} where they are not
78
+ # of type +Hash+
79
+ #
80
+ # @return [Array<Symbol>]
81
+ def parameters
82
+ parameters_list.reject do |param|
83
+ param.is_a?(Hash)
84
+ end
85
+ end
86
+
87
+ # Hash representing all parameters with default values
88
+ #
89
+ # These are filtered out from {#parameters_list} where they are
90
+ # of type +Hash+ and merged into a single hash
91
+ #
92
+ # @return [Hash]
93
+ def defaults
94
+ parameters_list.select do |param|
95
+ param.is_a?(Hash)
96
+ end.reduce(&:merge) || {}
97
+ end
98
+
99
+ # Parameters without default converted to final string
100
+ #
101
+ # {#extra} is added so that for normal parameters the parameter is returned
102
+ # and for named parameter +:+ is added
103
+ #
104
+ # @return [Array<String>]
105
+ def parameters_strings
106
+ return parameters.map(&:to_s) unless named?
107
+
108
+ parameters.map do |param|
109
+ "#{param}:"
110
+ end
111
+ end
112
+
113
+ # Strings representing all parameters with default value
114
+ #
115
+ # @return [Array<String>]
116
+ def defaults_strings
117
+ joinner = named? ? ': ' : ' = '
118
+ defaults.map do |key, value|
119
+ "#{key}#{joinner}#{value}"
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -7,6 +7,8 @@ class Sinclair
7
7
  #
8
8
  # Define an instance method from string
9
9
  class StringDefinition < MethodDefinition
10
+ build_with MethodBuilder::StringMethodBuilder
11
+
10
12
  # @param name [String,Symbol] name of the method
11
13
  # @param code [String] code to be evaluated as method
12
14
  # @param options [Hash] Options of construction
@@ -20,6 +22,30 @@ class Sinclair
20
22
  default_value :block?, false
21
23
  default_value :string?, true
22
24
 
25
+ # string with the code to be defined
26
+ #
27
+ # @return [String]
28
+ def code_definition
29
+ <<-CODE
30
+ def #{name}#{parameters_string}
31
+ #{code_line}
32
+ end
33
+ CODE
34
+ end
35
+
36
+ private
37
+
38
+ # @private
39
+ # String for parameters
40
+ #
41
+ # @return [String]
42
+ def parameters_string
43
+ ParameterBuilder.from(
44
+ options_object.parameters, options_object.named_parameters
45
+ )
46
+ end
47
+
48
+ # @private
23
49
  # codeline to be run inside the code
24
50
  #
25
51
  # @return [String]
@@ -27,8 +53,6 @@ class Sinclair
27
53
  cached? ? code_with_cache : code
28
54
  end
29
55
 
30
- private
31
-
32
56
  # @method code
33
57
  # @private
34
58
  #
@@ -10,8 +10,10 @@ class Sinclair
10
10
 
11
11
  autoload :BlockHelper, 'sinclair/method_definition/block_helper'
12
12
  autoload :BlockDefinition, 'sinclair/method_definition/block_definition'
13
- autoload :StringDefinition, 'sinclair/method_definition/string_definition'
14
13
  autoload :CallDefinition, 'sinclair/method_definition/call_definition'
14
+ autoload :StringDefinition, 'sinclair/method_definition/string_definition'
15
+ autoload :ParameterBuilder, 'sinclair/method_definition/parameter_builder'
16
+ autoload :ParameterHelper, 'sinclair/method_definition/parameter_helper'
15
17
 
16
18
  # @method name
17
19
  #
@@ -59,13 +61,47 @@ class Sinclair
59
61
  # The creation is based on type which will be used to infer
60
62
  # which subclass of {Sinclair::MethodDefinition} to be used
61
63
  #
64
+ # If type is +nil+ then call is delegated to {.from} which will infer the type
65
+ # from the arguments
66
+ #
62
67
  # @param type [Symbol] the method definition type
63
68
  #
64
69
  # @return [Sinclair::MethodDefinition] an instance of a subclass
65
70
  def for(type, *args, **options, &block)
71
+ return from(*args, **options, &block) unless type
72
+
66
73
  klass = const_get("#{type}_definition".camelize)
67
74
  klass.new(*args, **options, &block)
68
75
  end
76
+
77
+ # Defines builder for a definition class
78
+ #
79
+ # @param builder_class [Class<MethodBuilder>]
80
+ #
81
+ # @return [Symbol] constant +:build+
82
+ #
83
+ # @!macro build_with
84
+ # @api private
85
+ #
86
+ # @!method build(klass, type)
87
+ #
88
+ # Builds the method defined
89
+ #
90
+ # The method is built using {$1}
91
+ #
92
+ # @param klass [Class] The class where the method will be built
93
+ # @param type [Symbol] type of method to be built
94
+ # - {MethodBuilder::CLASS_METHOD} : A class method will be built
95
+ # - {MethodBuilder::INSTANCE_METHOD} : An instance method will be built
96
+ #
97
+ # @see $1#build
98
+ #
99
+ # @return [Symbol] the name of the method built
100
+ def build_with(builder_class)
101
+ define_method(:build) do |klass, type|
102
+ builder_class.build(klass, self, type: type)
103
+ end
104
+ end
69
105
  end
70
106
 
71
107
  # @param name [String,Symbol] name of the method
@@ -87,10 +123,13 @@ class Sinclair
87
123
  #
88
124
  # @example (see MethodDefinition::StringDefinition#build)
89
125
  # @example (see MethodDefinition::BlockDefinition#build)
126
+ # @example (see MethodDefinition::CallDefinition#build)
90
127
  #
91
128
  # @return [Symbol] name of the created method
92
- def build(_klass)
93
- raise 'Build is implemented in subclasses. ' \
129
+ #
130
+ # @raise NotImplementedError
131
+ def build(_klass, _type)
132
+ raise NotImplementedError, 'Build is implemented in subclasses. ' \
94
133
  "Use #{self.class}.from to initialize a proper object"
95
134
  end
96
135