modulation 0.15 → 0.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48d5edc681b84cf2e1c5774961f96c4e544ac0af84bbc5343e90f0fc4b1169d2
4
- data.tar.gz: ac8f8ed76ae68a98dae475b9eaba677208688120dd15f4429a4d82024469409b
3
+ metadata.gz: 12a1828939d249c28696d296dd5f835e20f2726e101986ca2c9b1168f4fa0ff7
4
+ data.tar.gz: 53bdc3d0b8cf352030689930b6cf506d144e32d66187ac0a9e4cd564b1a54cfc
5
5
  SHA512:
6
- metadata.gz: d1028fbba22d36fad82cc003d6958d1302479fddceafc3937f4154e81fad81ed944f487d9f44fe3afa66e4099ea73c3f5a42e73fabc5ff5ae6ef99acfe4ca7d0
7
- data.tar.gz: 7c4281980fed04ed041da843fded0e555e2d00b35b7f3feb28ef3ea5ac266821b9a007212a02dbab6e67fc6adce583607a2f6b4f1e0830c17b6a530c7b896547
6
+ metadata.gz: 9edce0c45cbb7885ee1150b85083d2972c54d784c4fc0f74fe020d11e48d916d50eea51efdc2a6250009132186d11071013f14eb15f0e9c1bfaf75d9337a95b5
7
+ data.tar.gz: 6311006815093c0f15caa25b735710268303a0090bd4fbc45e9e07737d054e40956438eeefaf1431ab62bb636580b42abe7ac908a27a150679f90cbfb4316f8d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ 0.16 2018-09-24
2
+ ---------------
3
+
4
+ * Add __expose! method for exposing private symbols for testing purposes
5
+
1
6
  0.15 2018-09-09
2
7
  ---------------
3
8
 
data/README.md CHANGED
@@ -29,6 +29,7 @@ code in a functional style, with a minimum of boilerplate code.
29
29
  code in wierd ways.
30
30
  - Allows [mocking of dependencies](#mocking-dependencies) for testing purposes.
31
31
  - Can be used to [write gems](#writing-gems-using-modulation).
32
+ - Facilitates [unit-testing](#unit-testing-modules) of private methods and constants.
32
33
 
33
34
  ## Rationale
34
35
 
@@ -266,6 +267,40 @@ end
266
267
  what = ::MEANING_OF_LIFE
267
268
  ```
268
269
 
270
+ ### Unit testing modules
271
+
272
+ Methods and constants that are not exported can be tested using the `__expose!`
273
+ method. Thus you can keep implementation details hidden, while being able to
274
+ easily test them:
275
+
276
+ *parser.rb*
277
+ ```ruby
278
+ export :parse
279
+
280
+ def parse(inp)
281
+ split(inp).map(&:to_sym)
282
+ end
283
+
284
+ # private method
285
+ def split(inp)
286
+ inp.split(',').map(&:strip)
287
+ end
288
+ ```
289
+
290
+ *test_seq.rb*
291
+ ```ruby
292
+ require 'modulation'
293
+ require 'minitest/autorun'
294
+
295
+ Parser = import('../lib/parser').__expose!
296
+
297
+ class FibTest < Minitest::Test
298
+ def test_that_split_trims_split_parts
299
+ assert_equal(%w[abc def ghi], Parser.split(' abc ,def , ghi '))
300
+ end
301
+ end
302
+ ```
303
+
269
304
  ### Mocking dependencies
270
305
 
271
306
  Modules loaded by Modulation can be easily mocked when running tests or specs,
@@ -290,8 +325,8 @@ end
290
325
  class UserControllerTest < Minitest::Test
291
326
  def test_user_storage
292
327
  Modulation.mock('../lib/storage', MockStorage) do
293
- controller = UserController.
294
- assert_equal
328
+ controller = UserController.new
329
+ ...
295
330
  end
296
331
  end
297
332
  end
@@ -370,32 +405,29 @@ MyFeature = import 'my_gem/my_feature'
370
405
 
371
406
  ## Coding style recommendations
372
407
 
373
- * Import modules into constants, not into variables:
408
+ * Import modules into constants, not variables:
374
409
 
375
410
  ```ruby
376
411
  Settings = import('./settings')
377
412
  ```
378
413
 
379
- * Place your exports at the top of your module:
414
+ * Place your exports at the top of your module, followed by `require`s,
415
+ followed by `import`s:
380
416
 
381
417
  ```ruby
382
418
  export :foo, :bar, :baz
383
419
 
384
- ...
385
- ```
386
-
387
- * Place your imports at the top of your module:
420
+ require 'json'
388
421
 
389
- ```ruby
390
- Foo = import('./foo')
391
- Bar = import('./bar')
392
- Baz = import('./baz')
422
+ Core = import('./core')
423
+
393
424
  ...
394
425
  ```
395
426
 
427
+
396
428
  ## Why you should not use Modulation
397
429
 
398
- - Modulation is (probably) not production-ready.
430
+ - Modulation is not production-ready.
399
431
  - Modulation is not thread-safe.
400
432
  - Modulation doesn't play well with rdoc/yard.
401
433
  - Modulation (probably) doesn't play well with `Marshal`.
@@ -52,15 +52,35 @@ module Modulation
52
52
  def set_exported_symbols(mod, symbols)
53
53
  mod.__module_info[:exported_symbols] = symbols
54
54
  singleton = mod.singleton_class
55
+
56
+ privatize_non_exported_methods(singleton, symbols)
57
+ expose_exported_constants(mod, singleton, symbols)
58
+ end
55
59
 
60
+ # Sets all non-exported methods as private for given module
61
+ # @param singleton [Class] sinleton for module
62
+ # @param symbols [Array] array of exported symbols
63
+ # @return [void]
64
+ def privatize_non_exported_methods(singleton, symbols)
56
65
  singleton.instance_methods(false).each do |sym|
57
66
  next if symbols.include?(sym)
58
67
  singleton.send(:private, sym)
59
68
  end
69
+ end
60
70
 
61
- singleton.constants.each do |sym|
62
- next unless symbols.include?(sym)
63
- mod.const_set(sym, singleton.const_get(sym))
71
+ # Copies exported constants from singleton to module
72
+ # @param mod [Module] module with exported symbols
73
+ # @param singleton [Class] sinleton for module
74
+ # @param symbols [Array] array of exported symbols
75
+ # @return [void]
76
+ def expose_exported_constants(mod, singleton, symbols)
77
+ private_constants = mod.__module_info[:private_constants] = []
78
+ singleton.constants(false).each do |sym|
79
+ if symbols.include?(sym)
80
+ mod.const_set(sym, singleton.const_get(sym))
81
+ else
82
+ private_constants << sym unless sym == :MODULE
83
+ end
64
84
  end
65
85
  end
66
86
 
@@ -18,14 +18,8 @@ class Module
18
18
  # @return [void]
19
19
  def extend_from(path)
20
20
  mod = import(path, caller(1..1).first)
21
- mod.singleton_class.instance_methods(false).each do |sym|
22
- self.class.send(:define_method, sym, mod.method(sym).to_proc)
23
- end
24
-
25
- mod.singleton_class.constants(false).each do |sym|
26
- next if sym == :MODULE
27
- const_set(sym, mod.singleton_class.const_get(sym))
28
- end
21
+ add_module_methods(mod, self.class)
22
+ add_module_constants(mod, self)
29
23
  end
30
24
 
31
25
  # Includes exported methods from the given file name in the receiver
@@ -34,15 +28,21 @@ class Module
34
28
  # @return [void]
35
29
  def include_from(path)
36
30
  mod = import(path, caller(1..1).first)
37
- exported_symbols = mod.__module_info[:exported_symbols]
31
+ add_module_methods(mod, self)
32
+ add_module_constants(mod, self)
33
+ end
38
34
 
35
+ def add_module_methods(mod, target)
39
36
  mod.singleton_class.instance_methods(false).each do |sym|
40
- send(:define_method, sym, &mod.method(sym))
37
+ target.send(:define_method, sym, &mod.method(sym))
41
38
  end
39
+ end
42
40
 
41
+ def add_module_constants(mod, target)
42
+ exported_symbols = mod.__module_info[:exported_symbols]
43
43
  mod.singleton_class.constants(false).each do |sym|
44
44
  next unless exported_symbols.include?(sym)
45
- const_set(sym, mod.singleton_class.const_get(sym))
45
+ target.const_set(sym, mod.singleton_class.const_get(sym))
46
46
  end
47
47
  end
48
48
  end
@@ -76,8 +76,24 @@ module Modulation
76
76
 
77
77
  # Allow modules to use attr_accessor/reader/writer and include methods by
78
78
  # forwarding calls to singleton_class
79
- [:attr_accessor, :attr_reader, :attr_writer, :include].each do |sym|
79
+ %i[attr_accessor attr_reader attr_writer include].each do |sym|
80
80
  define_method(sym) { |*args| singleton_class.send(sym, *args) }
81
81
  end
82
+
83
+ # Exposes all private methods and private constants as public
84
+ # @return [Module] self
85
+ def __expose!
86
+ singleton = singleton_class
87
+
88
+ singleton.private_instance_methods.each do |sym|
89
+ singleton.send(:public, sym)
90
+ end
91
+
92
+ __module_info[:private_constants].each do |sym|
93
+ const_set(sym, singleton.const_get(sym))
94
+ end
95
+
96
+ self
97
+ end
82
98
  end
83
99
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Modulation
4
- VERSION = '0.15'
5
- end
4
+ VERSION = '0.16'
5
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modulation
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.15'
4
+ version: '0.16'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-09 00:00:00.000000000 Z
11
+ date: 2018-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest