flutter 0.1.0.pre.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.overcommit.yml +8 -0
  3. data/.rubocop.yml +10 -0
  4. data/CHANGELOG.md +6 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Gemfile +35 -0
  7. data/Guardfile +9 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +134 -0
  10. data/Rakefile +24 -0
  11. data/TODO.md +30 -0
  12. data/integration_tests/minitest/grape_app/.gitignore +67 -0
  13. data/integration_tests/minitest/grape_app/.ruby-version +1 -0
  14. data/integration_tests/minitest/grape_app/Gemfile +17 -0
  15. data/integration_tests/minitest/grape_app/Gemfile.lock +89 -0
  16. data/integration_tests/minitest/grape_app/README.md +2 -0
  17. data/integration_tests/minitest/grape_app/Rakefile +9 -0
  18. data/integration_tests/minitest/grape_app/api/api.rb +34 -0
  19. data/integration_tests/minitest/grape_app/api/routes/api_helpers.rb +12 -0
  20. data/integration_tests/minitest/grape_app/api/routes/change_request/api.rb +69 -0
  21. data/integration_tests/minitest/grape_app/api/routes/change_request/response_entity.rb +18 -0
  22. data/integration_tests/minitest/grape_app/api/routes/event/api.rb +121 -0
  23. data/integration_tests/minitest/grape_app/api/routes/event/response_entity.rb +41 -0
  24. data/integration_tests/minitest/grape_app/api/routes/project/api.rb +59 -0
  25. data/integration_tests/minitest/grape_app/api/routes/project/response_entity.rb +13 -0
  26. data/integration_tests/minitest/grape_app/api/routes/property/api.rb +78 -0
  27. data/integration_tests/minitest/grape_app/api/routes/property/response_entity.rb +31 -0
  28. data/integration_tests/minitest/grape_app/api/routes/trackable_object/api.rb +64 -0
  29. data/integration_tests/minitest/grape_app/api/routes/trackable_object/response_entity.rb +24 -0
  30. data/integration_tests/minitest/grape_app/api/routes/tracking_spec/api.rb +88 -0
  31. data/integration_tests/minitest/grape_app/api/routes/tracking_spec/response_entity.rb +17 -0
  32. data/integration_tests/minitest/grape_app/api/routes/tracking_spec/spec_response.rb +19 -0
  33. data/integration_tests/minitest/grape_app/api/routes/user/api.rb +22 -0
  34. data/integration_tests/minitest/grape_app/api/routes/user/response_entity.rb +12 -0
  35. data/integration_tests/minitest/grape_app/app/app.rb +30 -0
  36. data/integration_tests/minitest/grape_app/app/change_request/endpoint.rb +24 -0
  37. data/integration_tests/minitest/grape_app/app/change_request/generate_system_changes.rb +25 -0
  38. data/integration_tests/minitest/grape_app/app/change_request/service.rb +79 -0
  39. data/integration_tests/minitest/grape_app/app/event/endpoint.rb +78 -0
  40. data/integration_tests/minitest/grape_app/app/event/service.rb +68 -0
  41. data/integration_tests/minitest/grape_app/app/event/validator.rb +108 -0
  42. data/integration_tests/minitest/grape_app/app/project/endpoint.rb +24 -0
  43. data/integration_tests/minitest/grape_app/app/project/service.rb +42 -0
  44. data/integration_tests/minitest/grape_app/app/property/endpoint.rb +39 -0
  45. data/integration_tests/minitest/grape_app/app/property/service.rb +56 -0
  46. data/integration_tests/minitest/grape_app/app/trackable_object/endpoint.rb +38 -0
  47. data/integration_tests/minitest/grape_app/app/trackable_object/service.rb +49 -0
  48. data/integration_tests/minitest/grape_app/app/trackable_object/validator.rb +40 -0
  49. data/integration_tests/minitest/grape_app/app/tracking_spec/endpoint.rb +58 -0
  50. data/integration_tests/minitest/grape_app/app/tracking_spec/expand_tracking_spec_events.rb +91 -0
  51. data/integration_tests/minitest/grape_app/app/tracking_spec/service.rb +51 -0
  52. data/integration_tests/minitest/grape_app/app/tracking_spec/validator.rb +61 -0
  53. data/integration_tests/minitest/grape_app/app/utils/errors/api_exceptions.rb +10 -0
  54. data/integration_tests/minitest/grape_app/app/utils/errors/service_exceptions.rb +12 -0
  55. data/integration_tests/minitest/grape_app/app/versioned_entity/entity.rb +110 -0
  56. data/integration_tests/minitest/grape_app/app/versioned_entity/service.rb +47 -0
  57. data/integration_tests/minitest/grape_app/app/versioned_entity/validator.rb +61 -0
  58. data/integration_tests/minitest/grape_app/app/versioned_entity_snapshot/entity.rb +32 -0
  59. data/integration_tests/minitest/grape_app/config/boot.rb +9 -0
  60. data/integration_tests/minitest/grape_app/config.ru +3 -0
  61. data/integration_tests/minitest/grape_app/db/proto/change_request.rb +10 -0
  62. data/integration_tests/minitest/grape_app/db/proto/common/doc.rb +86 -0
  63. data/integration_tests/minitest/grape_app/db/proto/event.rb +10 -0
  64. data/integration_tests/minitest/grape_app/db/proto/event_snapshot.rb +10 -0
  65. data/integration_tests/minitest/grape_app/db/proto/project.rb +21 -0
  66. data/integration_tests/minitest/grape_app/db/proto/property.rb +10 -0
  67. data/integration_tests/minitest/grape_app/db/proto/property_snapshot.rb +10 -0
  68. data/integration_tests/minitest/grape_app/db/proto/trackable_object.rb +10 -0
  69. data/integration_tests/minitest/grape_app/db/proto/trackable_object_snapshot.rb +10 -0
  70. data/integration_tests/minitest/grape_app/db/proto/tracking_spec.rb +10 -0
  71. data/integration_tests/minitest/grape_app/test/api/functional/event_test.rb +186 -0
  72. data/integration_tests/minitest/grape_app/test/api/functional/property_test.rb +197 -0
  73. data/integration_tests/minitest/grape_app/test/api/functional/trackable_object_test.rb +134 -0
  74. data/integration_tests/minitest/grape_app/test/api/functional/tracking_spec_test.rb +56 -0
  75. data/integration_tests/minitest/grape_app/test/api/functional/user_test.rb +24 -0
  76. data/integration_tests/minitest/grape_app/test/api/functional/versioned_entity_helper.rb +157 -0
  77. data/integration_tests/minitest/grape_app/test/fixtures/change_requests.rb +83 -0
  78. data/integration_tests/minitest/grape_app/test/fixtures/event_snapshots.rb +105 -0
  79. data/integration_tests/minitest/grape_app/test/fixtures/events.rb +58 -0
  80. data/integration_tests/minitest/grape_app/test/fixtures/properties.rb +66 -0
  81. data/integration_tests/minitest/grape_app/test/fixtures/property_snapshots.rb +124 -0
  82. data/integration_tests/minitest/grape_app/test/fixtures/sample_tracking_spec.json +125 -0
  83. data/integration_tests/minitest/grape_app/test/fixtures/trackable_objects.rb +58 -0
  84. data/integration_tests/minitest/grape_app/test/fixtures/trackable_objects_snapshots.rb +61 -0
  85. data/integration_tests/minitest/grape_app/test/fixtures/tracking_specs.rb +22 -0
  86. data/integration_tests/minitest/grape_app/test/test_helper.rb +15 -0
  87. data/lib/flutter/config.rb +24 -0
  88. data/lib/flutter/minitest.rb +83 -0
  89. data/lib/flutter/parser.rb +82 -0
  90. data/lib/flutter/persistence.rb +152 -0
  91. data/lib/flutter/rspec.rb +66 -0
  92. data/lib/flutter/tracker.rb +133 -0
  93. data/lib/flutter/version.rb +5 -0
  94. data/lib/flutter.rb +12 -0
  95. data/lib/minitest/flutter_plugin.rb +16 -0
  96. data/sig/flutter.rbs +4 -0
  97. metadata +187 -0
@@ -0,0 +1,124 @@
1
+ module Skee
2
+ module Test
3
+ module Fixtures
4
+ class PropertySnapshots
5
+ def self.all
6
+ [
7
+ {
8
+ 'id': '1',
9
+ 'name': 'name',
10
+ 'is_array': false,
11
+ 'property_of': :trackable_object,
12
+ 'data_type': 'String',
13
+ 'description': "user's name",
14
+ 'trackable_object_id': nil,
15
+ 'is_nullable': false,
16
+ 'additional_instructions': nil,
17
+ 'data_format_id': nil,
18
+ 'regex_validation': nil,
19
+ 'change_request_id': '1',
20
+ 'previous_snapshot_id': nil,
21
+ 'is_removed': nil,
22
+ },
23
+ {
24
+ 'id': '2',
25
+ 'name': 'created_at',
26
+ 'is_array': false,
27
+ 'property_of': :event,
28
+ 'data_type': 'String',
29
+ 'description': 'created_at timestamp',
30
+ 'trackable_object_id': nil,
31
+ 'is_nullable': false,
32
+ 'additional_instructions': nil,
33
+ 'data_format_id': nil,
34
+ 'regex_validation': nil,
35
+ 'change_request_id': '1',
36
+ 'previous_snapshot_id': nil,
37
+ 'is_removed': nil,
38
+ },
39
+ {
40
+ 'id': '3',
41
+ 'name': 'products',
42
+ 'is_array': true,
43
+ 'property_of': :trackable_object,
44
+ 'data_type': 'Array',
45
+ 'description': 'list of products',
46
+ 'trackable_object_id': '2',
47
+ 'is_nullable': true,
48
+ 'additional_instructions': nil,
49
+ 'data_format_id': nil,
50
+ 'regex_validation': nil,
51
+ 'change_request_id': '1',
52
+ 'previous_snapshot_id': nil,
53
+ 'is_removed': nil,
54
+ },
55
+ {
56
+ 'id': '4',
57
+ 'name': 'price',
58
+ 'is_array': false,
59
+ 'property_of': :trackable_object,
60
+ 'data_type': 'Float',
61
+ 'description': 'price of products',
62
+ 'trackable_object_id': nil,
63
+ 'is_nullable': true,
64
+ 'additional_instructions': nil,
65
+ 'data_format_id': nil,
66
+ 'regex_validation': nil,
67
+ 'change_request_id': '1',
68
+ 'previous_snapshot_id': nil,
69
+ 'is_removed': nil,
70
+ },
71
+ {
72
+ 'id': '5',
73
+ 'name': 'discount',
74
+ 'is_array': false,
75
+ 'property_of': :trackable_object,
76
+ 'data_type': 'Float',
77
+ 'description': 'discount on products',
78
+ 'trackable_object_id': nil,
79
+ 'is_nullable': true,
80
+ 'additional_instructions': nil,
81
+ 'data_format_id': nil,
82
+ 'regex_validation': nil,
83
+ 'change_request_id': '1',
84
+ 'previous_snapshot_id': '4',
85
+ 'is_removed': nil,
86
+ },
87
+ {
88
+ 'id': '6',
89
+ 'name': 'address',
90
+ 'is_array': false,
91
+ 'property_of': :trackable_object,
92
+ 'data_type': 'String',
93
+ 'description': 'A location address',
94
+ 'trackable_object_id': nil,
95
+ 'is_nullable': true,
96
+ 'additional_instructions': nil,
97
+ 'data_format_id': nil,
98
+ 'regex_validation': nil,
99
+ 'change_request_id': '1',
100
+ 'previous_snapshot_id': '4',
101
+ 'is_removed': nil,
102
+ },
103
+ {
104
+ 'id': '7',
105
+ 'name': 'store_name',
106
+ 'is_array': false,
107
+ 'property_of': :trackable_object,
108
+ 'data_type': 'String',
109
+ 'description': 'Store name',
110
+ 'trackable_object_id': nil,
111
+ 'is_nullable': true,
112
+ 'additional_instructions': nil,
113
+ 'data_format_id': nil,
114
+ 'regex_validation': nil,
115
+ 'change_request_id': '1',
116
+ 'previous_snapshot_id': '4',
117
+ 'is_removed': nil,
118
+ }
119
+ ]
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,125 @@
1
+ [
2
+ {
3
+ "name": "Closed App",
4
+ "description": "Description of event A.",
5
+ "payload": {
6
+ "primary_user": {
7
+ "name": {
8
+ "data_type": "String",
9
+ "description": "user's name",
10
+ "is_nullable": false,
11
+ "additional_instructions": null,
12
+ "data_format_id": null,
13
+ "regex_validation": null,
14
+ "is_removed": null
15
+ }
16
+ },
17
+ "created_timestamp": {
18
+ "data_type": "String",
19
+ "description": "created_at timestamp",
20
+ "is_nullable": false,
21
+ "additional_instructions": null,
22
+ "data_format_id": null,
23
+ "regex_validation": null,
24
+ "is_removed": null
25
+ },
26
+ "cart": {
27
+ "products": [
28
+ {
29
+ "name": {
30
+ "data_type": "String",
31
+ "description": "user's name",
32
+ "is_nullable": false,
33
+ "additional_instructions": null,
34
+ "data_format_id": null,
35
+ "regex_validation": null,
36
+ "is_removed": null
37
+ }
38
+ }
39
+ ]
40
+ }
41
+ }
42
+ },
43
+ {
44
+ "name": "Opened App",
45
+ "description": "Description of event A.",
46
+ "payload": {
47
+ "primary_user": {
48
+ "name": {
49
+ "data_type": "String",
50
+ "description": "user's name",
51
+ "is_nullable": false,
52
+ "additional_instructions": null,
53
+ "data_format_id": null,
54
+ "regex_validation": null,
55
+ "is_removed": null
56
+ }
57
+ },
58
+ "created_timestamp": {
59
+ "data_type": "String",
60
+ "description": "created_at timestamp",
61
+ "is_nullable": false,
62
+ "additional_instructions": null,
63
+ "data_format_id": null,
64
+ "regex_validation": null,
65
+ "is_removed": null
66
+ },
67
+ "cart": {
68
+ "products": [
69
+ {
70
+ "name": {
71
+ "data_type": "String",
72
+ "description": "user's name",
73
+ "is_nullable": false,
74
+ "additional_instructions": null,
75
+ "data_format_id": null,
76
+ "regex_validation": null,
77
+ "is_removed": null
78
+ }
79
+ }
80
+ ]
81
+ }
82
+ }
83
+ },
84
+ {
85
+ "name": "Launched App",
86
+ "description": "Description of event A.",
87
+ "payload": {
88
+ "primary_user": {
89
+ "name": {
90
+ "data_type": "String",
91
+ "description": "user's name",
92
+ "is_nullable": false,
93
+ "additional_instructions": null,
94
+ "data_format_id": null,
95
+ "regex_validation": null,
96
+ "is_removed": null
97
+ }
98
+ },
99
+ "created_timestamp": {
100
+ "data_type": "String",
101
+ "description": "created_at timestamp",
102
+ "is_nullable": false,
103
+ "additional_instructions": null,
104
+ "data_format_id": null,
105
+ "regex_validation": null,
106
+ "is_removed": null
107
+ },
108
+ "cart": {
109
+ "products": [
110
+ {
111
+ "name": {
112
+ "data_type": "String",
113
+ "description": "user's name",
114
+ "is_nullable": false,
115
+ "additional_instructions": null,
116
+ "data_format_id": null,
117
+ "regex_validation": null,
118
+ "is_removed": null
119
+ }
120
+ }
121
+ ]
122
+ }
123
+ }
124
+ }
125
+ ]
@@ -0,0 +1,58 @@
1
+ module Skee
2
+ module Test
3
+ module Fixtures
4
+ class TrackableObjects
5
+ def self.all
6
+ [{
7
+ 'id': '1',
8
+ 'latest_snapshot_id': '1',
9
+ 'snapshot_mapping': {
10
+ '0' => '1'
11
+ },
12
+ 'is_removed': nil,
13
+ }, {
14
+ 'id': '2',
15
+ 'latest_snapshot_id': '2',
16
+ 'snapshot_mapping': {
17
+ '0' => '2'
18
+ },
19
+ 'is_removed': nil,
20
+ }, {
21
+ 'id': '3',
22
+ 'latest_snapshot_id': '3',
23
+ 'snapshot_mapping': {
24
+ '0' => '3'
25
+ },
26
+ 'is_removed': nil,
27
+ }, {
28
+ 'id': '4',
29
+ 'latest_snapshot_id': '4',
30
+ 'snapshot_mapping': {
31
+ },
32
+ 'is_removed': nil,
33
+ }, {
34
+ 'id': '5',
35
+ 'latest_snapshot_id': '4',
36
+ 'snapshot_mapping': {
37
+ },
38
+ 'is_removed': nil,
39
+ }, {
40
+ 'id': '6',
41
+ 'latest_snapshot_id': '6',
42
+ 'snapshot_mapping': {
43
+ '0' => '6'
44
+ },
45
+ 'is_removed': nil,
46
+ }, {
47
+ 'id': '7',
48
+ 'latest_snapshot_id': nil,
49
+ 'snapshot_mapping': {
50
+ },
51
+ 'is_removed': nil,
52
+ }
53
+ ]
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,61 @@
1
+ module Skee
2
+ module Test
3
+ module Fixtures
4
+ class TrackableObjectSnapshots
5
+ def self.all
6
+ [{
7
+ 'id': '1',
8
+ 'name': 'User',
9
+ 'change_request_id': '16',
10
+ 'previous_snapshot_id': nil,
11
+ 'property_ids': ['1'],
12
+ 'is_removed': nil,
13
+ }, {
14
+ 'id': '2',
15
+ 'name': 'Product',
16
+ 'change_request_id': '12',
17
+ 'previous_snapshot_id': nil,
18
+ 'property_ids': ['4'],
19
+ 'is_removed': nil,
20
+ }, {
21
+ 'id': '3',
22
+ 'name': 'Cart',
23
+ 'change_request_id': '16',
24
+ 'previous_snapshot_id': '15',
25
+ 'property_ids': ['3'],
26
+ 'is_removed': nil,
27
+ }, {
28
+ 'id': '4',
29
+ 'name': 'Shop',
30
+ 'change_request_id': '14',
31
+ 'previous_snapshot_id': '15',
32
+ 'property_ids': [],
33
+ 'is_removed': nil,
34
+ }, {
35
+ 'id': '5',
36
+ 'name': 'Shops',
37
+ 'change_request_id': '18',
38
+ 'previous_snapshot_id': '4',
39
+ 'property_ids': [],
40
+ 'is_removed': nil,
41
+ }, {
42
+ 'id': '6',
43
+ 'name': 'Store',
44
+ 'change_request_id': '18',
45
+ 'previous_snapshot_id': nil,
46
+ 'property_ids': ['7', '8'],
47
+ 'is_removed': nil,
48
+ }, {
49
+ 'id': '7',
50
+ 'name': 'Signed-in User',
51
+ 'change_request_id': '1',
52
+ 'previous_snapshot_id': nil,
53
+ 'property_ids': ['1'],
54
+ 'is_removed': nil,
55
+ }
56
+ ]
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,22 @@
1
+ module Skee
2
+ module Test
3
+ module Fixtures
4
+ class TrackingSpecs
5
+ def self.all
6
+ [
7
+ {
8
+ 'id': '1',
9
+ 'identifier': 'Solarbreeze',
10
+ 'version': 'v1',
11
+ 'project_id': '1',
12
+ 'change_request_id': '1',
13
+ 'prev_tracking_spec_id': nil,
14
+ 'status': 'current',
15
+ 'events': ['1', '3', '4'],
16
+ }
17
+ ]
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ require 'rack/test'
2
+ require 'minitest/autorun'
3
+ require 'flutter'
4
+ require 'flutter/persistence'
5
+
6
+ require_relative '../config/boot'
7
+
8
+ Flutter.configure do |config|
9
+ config.enabled = true
10
+ end
11
+
12
+
13
+ Dir.glob(File.join(__dir__, 'fixtures/*.rb')).each do |f|
14
+ require_relative f unless File.absolute_path(f) == __FILE__
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/configurable"
4
+ require_relative "persistence"
5
+
6
+ module Flutter
7
+ module Config
8
+ class << self
9
+ private
10
+
11
+ def included(other)
12
+ other.extend(Dry::Configurable)
13
+ other.class_eval do
14
+ setting(:enabled, default: false, reader: true)
15
+ setting(:sources, default: Set.new(["#{Dir.pwd}/*"]))
16
+ setting(:exclusions, default: Set.new(["./vendor/*"]))
17
+ setting(:storage_class, default: Flutter::Persistence::Marshal)
18
+ setting(:storage_options, default: { path: "./.flutter" })
19
+ setting(:reset_storage, default: false)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "tracker"
4
+
5
+ begin
6
+ require "minitest"
7
+ rescue LoadError # rubocop:disable Lint/SuppressedException
8
+ end
9
+
10
+ module Flutter
11
+ module Minitest
12
+ class << self
13
+ attr_accessor :filtered
14
+
15
+ def flutter_tracker
16
+ @tracker ||= Flutter::Tracker.new(
17
+ Flutter.config.sources, Flutter.config.exclusions,
18
+ Flutter.config.storage_class, Flutter.config.storage_options
19
+ )
20
+ end
21
+ end
22
+
23
+ if defined?(::Minitest::AbstractReporter)
24
+ class Reporter < ::Minitest::AbstractReporter
25
+ def initialize(options)
26
+ super()
27
+ @verbose = options[:verbose]
28
+ end
29
+
30
+ def report
31
+ return unless ::Flutter.enabled
32
+
33
+ Flutter::Minitest.flutter_tracker.persist!
34
+ $stdout.puts "Flutter filtered out #{Flutter::Minitest.filtered} tests"
35
+ if @verbose
36
+ $stdout.puts "Persisted flutter #{Flutter::Minitest.flutter_tracker}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ module Hooks
43
+ module ClassMethods
44
+ def runnable_methods
45
+ Flutter::Minitest.filtered ||= 0
46
+ default = super()
47
+ default.select do |test|
48
+ skip = Minitest.flutter_tracker.skip?(
49
+ "#{name}##{test}",
50
+ File.absolute_path(instance_method(test).source_location[0]),
51
+ instance_method(test).source,
52
+ )
53
+ if skip
54
+ Flutter::Minitest.filtered += 1
55
+ end
56
+ !skip
57
+ end
58
+ end
59
+ end
60
+
61
+ def after_setup
62
+ super
63
+ Minitest.flutter_tracker&.start(location) if ::Flutter.enabled
64
+ end
65
+
66
+ def before_teardown
67
+ Minitest.flutter_tracker&.stop if ::Flutter.enabled
68
+ super
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ if defined?(Minitest::Test)
75
+ module Minitest
76
+ class Test
77
+ prepend Flutter::Minitest::Hooks
78
+ class << self
79
+ prepend Flutter::Minitest::Hooks::ClassMethods
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parser/current"
4
+ require "digest/sha1"
5
+ require "set"
6
+
7
+ module Flutter
8
+ class Parser
9
+ attr_reader :signatures
10
+
11
+ def initialize(file)
12
+ @signatures = {}
13
+ @targets = Set.new
14
+ if File.exist?(file)
15
+ code = File.open(file, "r").read
16
+ @file = File.absolute_path(file)
17
+ @ast = ::Parser::CurrentRuby.parse(code)
18
+ collect_targets(nil, nil, false)
19
+ build_signatures
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def collect_targets(ast, parent, in_singleton)
26
+ ast ||= @ast
27
+ if [:module, :class].include?(ast.type)
28
+ @targets << ast.location.name.source unless parent
29
+ parent ||= ast.location.name.source
30
+ end
31
+ ast.children.each do |child|
32
+ if child && (child.class == ::Parser::AST::Node)
33
+ if [:class, :module].include?(child.type)
34
+ child_name = child.location.name.source
35
+ full_name = parent && !child_name.start_with?("::") ? "#{parent}::#{child_name}" : child_name
36
+ @targets << full_name
37
+ collect_targets(child, full_name, false)
38
+ else
39
+ # Don't descend into local scopes as those classes/modules are not relevant
40
+ next if [:def, :defs, :block].include?(child.type)
41
+
42
+ collect_targets(child, parent, in_singleton || child.type == :sclass)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def build_signatures
49
+ require_relative @file
50
+ @targets.each do |container|
51
+ instance = Kernel.const_get(container)
52
+ class_methods = (
53
+ instance.methods - Object.methods
54
+ ) + (
55
+ instance.private_methods - Object.private_methods
56
+ )
57
+ instance_methods = (
58
+ instance.instance_methods - Object.instance_methods
59
+ ) + (
60
+ instance.private_instance_methods - Object.private_instance_methods
61
+ )
62
+
63
+ @signatures.merge!(class_methods.map do |method|
64
+ ["#{container}::#{method}", source_hash(instance.method(method))]
65
+ end.to_h)
66
+ @signatures.merge!(instance_methods.map do |method|
67
+ ["#{container}:#{method}", source_hash(instance.instance_method(method))]
68
+ end.to_h)
69
+ rescue NameError
70
+ $stderr.puts "failed to load #{container} in #{@file}"
71
+ end
72
+ rescue LoadError
73
+ $stderr.puts "failed to inspect #{@file}"
74
+ end
75
+
76
+ def source_hash(callable)
77
+ Digest::SHA1.hexdigest(callable.source)
78
+ rescue MethodSource::SourceNotFoundError
79
+ "<no-source>"
80
+ end
81
+ end
82
+ end