konstructor 0.2.0 → 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 +4 -4
- data/README.md +68 -99
- data/lib/konstructor/exceptions.rb +8 -1
- data/lib/konstructor/factory.rb +31 -18
- data/lib/konstructor/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a639335de2f050fdd01c8504178cc934204ada8
|
4
|
+
data.tar.gz: d6a81f610da6159060b39b9209a4a0913b2fe7c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae74c9aedf9971bd55fd2d709014631f39d1cff931d3d460b8084f30de57fe881599b264c657aa5431178d2aea5fa900d6820bc32e76c9b6f91422788f0e7707
|
7
|
+
data.tar.gz: eb8be3d92e5c96bd2a15580f8ec1dde0bf5d0a0648f58eabec393d321e27ffc74f3871aa62e931074f2e52cec1f58bb065dec898390c5b3e23dae5a9595bd6a7
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
Konstructor is a small gem that gives you multiple
|
12
12
|
constructors in Ruby.
|
13
13
|
|
14
|
-
Use `konstructor` keyword to
|
14
|
+
Use `konstructor` keyword to declare constructors additional to the defaul one:
|
15
15
|
```ruby
|
16
16
|
class SomeClass
|
17
17
|
konstructor
|
@@ -44,40 +44,43 @@ You can also install it without Bundler:
|
|
44
44
|
|
45
45
|
$ gem install konstructor
|
46
46
|
|
47
|
-
If you wish to manually include
|
48
|
-
you need it, see
|
47
|
+
If you are a gem author or just wish to manually include `konstructor`
|
48
|
+
keyword in your classes only when you need it, see
|
49
|
+
[Manual include](https://github.com/snovity/konstructor/wiki/Manual-include) page.
|
49
50
|
|
50
51
|
## Usage
|
51
52
|
|
52
|
-
In simplest form `konstructor` creates a
|
53
|
+
In its simplest form `konstructor` declaration creates a
|
54
|
+
constructor from the next method.
|
53
55
|
|
54
56
|
```ruby
|
55
|
-
|
56
|
-
|
57
|
-
|
57
|
+
konstructor
|
58
|
+
def create
|
59
|
+
end
|
58
60
|
|
59
|
-
|
60
|
-
|
61
|
-
|
61
|
+
konstructor
|
62
|
+
def recreate
|
63
|
+
end
|
62
64
|
```
|
63
65
|
|
64
66
|
When method names are given, it creates constructors from
|
65
67
|
those methods without affecting the next method.
|
66
68
|
|
67
69
|
```ruby
|
68
|
-
|
70
|
+
konstructor :create, :recreate
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
+
def not_constructor
|
73
|
+
end
|
72
74
|
|
73
|
-
|
74
|
-
|
75
|
+
def create
|
76
|
+
end
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
+
def recreate
|
79
|
+
end
|
78
80
|
```
|
79
81
|
|
80
|
-
|
82
|
+
Declaration with method names can be placed anywhere in
|
83
|
+
class definition.
|
81
84
|
|
82
85
|
```ruby
|
83
86
|
def create
|
@@ -89,7 +92,7 @@ those methods without affecting the next method.
|
|
89
92
|
end
|
90
93
|
```
|
91
94
|
|
92
|
-
In all above cases
|
95
|
+
In all above cases `SomeClass` will have the default constructor
|
93
96
|
and two additional ones.
|
94
97
|
|
95
98
|
```ruby
|
@@ -98,18 +101,28 @@ those methods without affecting the next method.
|
|
98
101
|
obj2 = SomeClass.recreate
|
99
102
|
```
|
100
103
|
|
101
|
-
|
104
|
+
If you decide to remove the default Ruby constructor for some reason,
|
105
|
+
you can effectively do it by marking it as private using Ruby
|
106
|
+
method `private_class_method`:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
class SomeClass
|
110
|
+
private_class_method :new
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
#### Same as default constructor
|
102
115
|
|
103
|
-
Additional constructors work exactly the same way as
|
104
|
-
built-in Ruby constructor.
|
116
|
+
Additional constructors work exactly the same way as the default one.
|
105
117
|
|
106
118
|
You can pass blocks to them.
|
107
119
|
|
108
120
|
```ruby
|
109
|
-
konstructor
|
110
|
-
def create(val)
|
111
|
-
|
112
|
-
end
|
121
|
+
konstructor
|
122
|
+
def create(val)
|
123
|
+
@val = yield val
|
124
|
+
end
|
125
|
+
#...
|
113
126
|
|
114
127
|
obj = SomeClass.create(3) { |v| v*3 }
|
115
128
|
obj.val # 9
|
@@ -135,64 +148,45 @@ end
|
|
135
148
|
obj = SomeSubclass.create(2, 3)
|
136
149
|
obj.val # 6
|
137
150
|
```
|
138
|
-
Once method is
|
139
|
-
it is always a
|
140
|
-
|
141
|
-
Methods inherited from superclasses can't become konstructors in
|
142
|
-
subclasses. To achieve the effect, define a new method,
|
143
|
-
mark it as konstructor and call the inherited one.
|
144
|
-
|
145
|
-
### Reserved names
|
146
|
-
|
147
|
-
Using reserved method names `new` and `initialize` for additional
|
148
|
-
constructor declaration will raise an error:
|
149
|
-
```ruby
|
150
|
-
konstructor
|
151
|
-
def initialize # raises Konstructor::ReservedNameError
|
152
|
-
end
|
153
|
-
```
|
154
|
-
or
|
155
|
-
```ruby
|
156
|
-
konstructor
|
157
|
-
def new # raises Konstructor::ReservedNameError
|
158
|
-
end
|
159
|
-
```
|
160
|
-
|
161
|
-
### Defining konstructors in Modules
|
151
|
+
Once method is declared as `konstructor` in hierarchy,
|
152
|
+
it is always a constructor.
|
162
153
|
|
163
|
-
|
164
|
-
|
154
|
+
There are certain limitations to what can be declared as `konstructor`,
|
155
|
+
see
|
156
|
+
[Limitations page](https://github.com/snovity/konstructor/wiki/Limitations)
|
157
|
+
for details.
|
165
158
|
|
166
|
-
|
159
|
+
#### Using with other gems
|
167
160
|
|
168
161
|
Konstructor doesn't affect other gems, including those
|
169
|
-
that depend on metaprogramming, such as
|
162
|
+
that depend on metaprogramming, such as
|
163
|
+
[rake](https://github.com/ruby/rake),
|
164
|
+
[thor](https://github.com/erikhuda/thor),
|
165
|
+
[contracts](https://github.com/egonSchiele/contracts.ruby), etc.
|
170
166
|
|
171
|
-
For instnace, this is how Konstructor works with contracts
|
167
|
+
For instnace, this is how Konstructor works with contracts:
|
172
168
|
```ruby
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
169
|
+
class SomeClass
|
170
|
+
konstructor
|
171
|
+
Contract Num => SomeClass
|
172
|
+
def create(some_number)
|
173
|
+
@number = some_number
|
174
|
+
end
|
175
|
+
end
|
180
176
|
```
|
181
177
|
|
182
178
|
If you stumble upon a metaprogramming gem that
|
183
|
-
conflicts with Konstructor, please
|
179
|
+
conflicts with Konstructor, please
|
180
|
+
[open an issue](https://github.com/snovity/konstructor/issues/new).
|
181
|
+
|
182
|
+
## Details
|
184
183
|
|
185
|
-
|
184
|
+
Ruby constructor is a pair consisting of public factory method defined
|
185
|
+
on a class and a private instance method. Therefore, to achieve
|
186
|
+
its goal `konstructor` marks instance method as private and defines a
|
187
|
+
corresponding public class method with the same name.
|
186
188
|
|
187
|
-
|
188
|
-
by marking it as private:
|
189
|
-
```ruby
|
190
|
-
class SomeClass
|
191
|
-
private_class_method :new
|
192
|
-
end
|
193
|
-
```
|
194
|
-
|
195
|
-
## Performance
|
189
|
+
#### Performance
|
196
190
|
|
197
191
|
Konstructor does all its work when class is being defined. Once class
|
198
192
|
has been defined, it's just standard Ruby instance creation.
|
@@ -200,34 +194,9 @@ Therefore, there is no runtime performance penalty.
|
|
200
194
|
|
201
195
|
Konstructor doesn't depend on other gems.
|
202
196
|
|
203
|
-
|
197
|
+
#### Thread safety
|
204
198
|
|
205
199
|
Konstructor is thread safe.
|
206
|
-
|
207
|
-
## Details
|
208
|
-
|
209
|
-
Ruby constructor is a pair consisting of public factory method defined
|
210
|
-
on a class and a private instance method. Therefore, to achieve
|
211
|
-
its goal `konstructor` marks instance method as private and defines a
|
212
|
-
corresponding public class method with the same name.
|
213
|
-
|
214
|
-
You can check if certain instance method name has been declared as
|
215
|
-
constructor or is a default constructor by running.
|
216
|
-
```ruby
|
217
|
-
Konstructor.is?(SomeClass, :initialize) # true
|
218
|
-
Konstructor.is?(SomeClass, :create) # true
|
219
|
-
Konstructor.is?(SomeClass, :recreate) # true
|
220
|
-
Konstructor.is?(SomeClass, :something_else) # false
|
221
|
-
```
|
222
|
-
|
223
|
-
It will return true even if no such constructor has
|
224
|
-
been defined yet. Like:
|
225
|
-
```ruby
|
226
|
-
class SomeClass
|
227
|
-
konstructor :create
|
228
|
-
end
|
229
|
-
```
|
230
|
-
Konstructor body may be supplied in subclasses.
|
231
200
|
|
232
201
|
## Contributing
|
233
202
|
|
@@ -10,7 +10,14 @@ module Konstructor
|
|
10
10
|
class IncludeInModuleError < StandardError
|
11
11
|
def initialize(base)
|
12
12
|
super "Konstructor can't be included in module '#{base.name}' directly, " +
|
13
|
-
"please, use ActiveSupport::Concern or included hook
|
13
|
+
"please, use ActiveSupport::Concern or standard included hook."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class DeclaringInheritedError < StandardError
|
18
|
+
def initialize(name)
|
19
|
+
super "You are declaring an inherited method '#{name}' as konstructor, "
|
20
|
+
"this is not allowed."
|
14
21
|
end
|
15
22
|
end
|
16
23
|
|
data/lib/konstructor/factory.rb
CHANGED
@@ -29,9 +29,9 @@ module Konstructor
|
|
29
29
|
if @next_method_is_konstructor
|
30
30
|
@next_method_is_konstructor = false
|
31
31
|
@konstructor_names << name
|
32
|
-
|
32
|
+
process_declaration(name)
|
33
33
|
elsif declared?(name)
|
34
|
-
|
34
|
+
process_declaration(name)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -58,29 +58,41 @@ module Konstructor
|
|
58
58
|
@konstructor_names.concat(new_names)
|
59
59
|
|
60
60
|
new_names.each do |name|
|
61
|
-
if
|
62
|
-
|
61
|
+
if method_in_hierarchy?(name)
|
62
|
+
process_declaration(name)
|
63
63
|
else
|
64
|
-
# not sure if
|
65
|
-
# but
|
66
|
-
validate_name(name)
|
64
|
+
# not sure if method will ever be defined,
|
65
|
+
# but validating its name anyway
|
66
|
+
validate_name!(name)
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
def
|
72
|
-
method_defined
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
71
|
+
def method_in_hierarchy?(name)
|
72
|
+
method_defined?(@klass, name)
|
73
|
+
end
|
74
|
+
|
75
|
+
def method_on_superclass?(name)
|
76
|
+
@klass.respond_to?(:superclass) && method_defined?(@klass.superclass, name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def method_defined?(klass, name)
|
80
|
+
klass.method_defined?(name) || klass.private_method_defined?(name)
|
77
81
|
end
|
78
82
|
|
79
83
|
# this method is idempotent
|
80
|
-
def
|
81
|
-
validate_name(name)
|
84
|
+
def process_declaration(name)
|
85
|
+
validate_name!(name)
|
82
86
|
|
83
|
-
|
87
|
+
if method_on_superclass?(name) && !declared_in_superclass?(name)
|
88
|
+
raise DeclaringInheritedError, name
|
89
|
+
end
|
90
|
+
|
91
|
+
define_factory(name)
|
92
|
+
mark_as_private(name)
|
93
|
+
end
|
94
|
+
|
95
|
+
def define_factory(name)
|
84
96
|
@klass.instance_eval <<-RUBY, __FILE__, __LINE__ + 1
|
85
97
|
def #{name}(*args, &block)
|
86
98
|
instance = allocate
|
@@ -88,12 +100,13 @@ module Konstructor
|
|
88
100
|
instance
|
89
101
|
end
|
90
102
|
RUBY
|
103
|
+
end
|
91
104
|
|
92
|
-
|
105
|
+
def mark_as_private(name)
|
93
106
|
@klass.__send__(:private, name)
|
94
107
|
end
|
95
108
|
|
96
|
-
def validate_name(name)
|
109
|
+
def validate_name!(name)
|
97
110
|
if Konstructor.reserved?(name)
|
98
111
|
raise ReservedNameError, name
|
99
112
|
end
|
data/lib/konstructor/version.rb
CHANGED