modulation 0.15 → 0.16

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 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