crystalball 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.rubocop.yml +4 -0
  4. data/.travis.yml +1 -1
  5. data/CHANGELOG.md +18 -0
  6. data/LICENSE +22 -674
  7. data/README.md +13 -158
  8. data/crystalball.gemspec +6 -2
  9. data/docs/img/favicon.ico +0 -0
  10. data/docs/img/logo.png +0 -0
  11. data/docs/index.md +44 -0
  12. data/docs/map_generators.md +149 -0
  13. data/docs/predictors.md +75 -0
  14. data/docs/runner.md +24 -0
  15. data/lib/crystalball.rb +8 -3
  16. data/lib/crystalball/active_record.rb +4 -0
  17. data/lib/crystalball/example_group_map.rb +19 -0
  18. data/lib/crystalball/execution_map.rb +17 -16
  19. data/lib/crystalball/extensions/git.rb +4 -0
  20. data/lib/crystalball/extensions/git/base.rb +14 -0
  21. data/lib/crystalball/extensions/git/lib.rb +18 -0
  22. data/lib/crystalball/factory_bot.rb +3 -0
  23. data/lib/crystalball/git_repo.rb +2 -7
  24. data/lib/crystalball/logging.rb +51 -0
  25. data/lib/crystalball/map_generator.rb +5 -7
  26. data/lib/crystalball/map_generator/allocated_objects_strategy.rb +6 -5
  27. data/lib/crystalball/map_generator/allocated_objects_strategy/object_tracker.rb +1 -0
  28. data/lib/crystalball/map_generator/base_strategy.rb +5 -4
  29. data/lib/crystalball/map_generator/configuration.rb +1 -1
  30. data/lib/crystalball/map_generator/coverage_strategy.rb +6 -5
  31. data/lib/crystalball/map_generator/described_class_strategy.rb +5 -5
  32. data/lib/crystalball/map_generator/factory_bot_strategy.rb +59 -0
  33. data/lib/crystalball/map_generator/factory_bot_strategy/dsl_patch.rb +40 -0
  34. data/lib/crystalball/map_generator/factory_bot_strategy/dsl_patch/factory_path_fetcher.rb +30 -0
  35. data/lib/crystalball/map_generator/factory_bot_strategy/factory_gem_loader.rb +27 -0
  36. data/lib/crystalball/map_generator/factory_bot_strategy/factory_runner_patch.rb +25 -0
  37. data/lib/crystalball/map_generator/parser_strategy.rb +60 -0
  38. data/lib/crystalball/map_generator/parser_strategy/processor.rb +129 -0
  39. data/lib/crystalball/map_generator/strategies_collection.rb +9 -9
  40. data/lib/crystalball/map_storage/yaml_storage.rb +7 -6
  41. data/lib/crystalball/prediction.rb +12 -11
  42. data/lib/crystalball/predictor.rb +7 -5
  43. data/lib/crystalball/predictor/associated_specs.rb +9 -4
  44. data/lib/crystalball/predictor/helpers/affected_example_groups_detector.rb +20 -0
  45. data/lib/crystalball/predictor/helpers/path_formatter.rb +18 -0
  46. data/lib/crystalball/predictor/modified_execution_paths.rb +11 -5
  47. data/lib/crystalball/predictor/modified_specs.rb +8 -2
  48. data/lib/crystalball/predictor/modified_support_specs.rb +39 -0
  49. data/lib/crystalball/predictor/strategy.rb +16 -0
  50. data/lib/crystalball/predictor_evaluator.rb +1 -1
  51. data/lib/crystalball/rails.rb +1 -0
  52. data/lib/crystalball/rails/helpers/base_schema_parser.rb +51 -0
  53. data/lib/crystalball/rails/helpers/schema_definition_parser.rb +36 -0
  54. data/lib/crystalball/rails/helpers/schema_definition_parser/active_record.rb +21 -0
  55. data/lib/crystalball/rails/helpers/schema_definition_parser/table_content_parser.rb +27 -0
  56. data/lib/crystalball/rails/map_generator/action_view_strategy.rb +5 -5
  57. data/lib/crystalball/rails/map_generator/action_view_strategy/patch.rb +1 -1
  58. data/lib/crystalball/rails/map_generator/i18n_strategy.rb +5 -5
  59. data/lib/crystalball/rails/map_generator/i18n_strategy/simple_patch.rb +1 -0
  60. data/lib/crystalball/rails/predictor/modified_schema.rb +81 -0
  61. data/lib/crystalball/rails/tables_map.rb +53 -0
  62. data/lib/crystalball/rails/tables_map_generator.rb +84 -0
  63. data/lib/crystalball/rails/tables_map_generator/configuration.rb +39 -0
  64. data/lib/crystalball/rspec/filtering.rb +52 -0
  65. data/lib/crystalball/rspec/prediction_builder.rb +21 -11
  66. data/lib/crystalball/rspec/prediction_pruning.rb +56 -0
  67. data/lib/crystalball/rspec/prediction_pruning/examples_pruner.rb +70 -0
  68. data/lib/crystalball/rspec/runner.rb +39 -27
  69. data/lib/crystalball/rspec/runner/configuration.rb +24 -14
  70. data/lib/crystalball/rspec/standard_prediction_builder.rb +17 -0
  71. data/lib/crystalball/source_diff.rb +12 -2
  72. data/lib/crystalball/source_diff/file_diff.rb +1 -1
  73. data/lib/crystalball/source_diff/formatting_checker.rb +50 -0
  74. data/lib/crystalball/version.rb +1 -1
  75. data/mkdocs.yml +23 -0
  76. metadata +102 -7
  77. data/lib/crystalball/case_map.rb +0 -19
  78. data/lib/crystalball/simple_predictor.rb +0 -18
data/docs/runner.md ADDED
@@ -0,0 +1,24 @@
1
+ ## RSpec Runner
2
+
3
+ Crystalball has a custom RSpec runner you can use in your development with `bundle exec crystalball` command. It builds a prediction and runs it.
4
+
5
+ ### Runner Configuration
6
+
7
+ #### Config file
8
+
9
+ Create a YAML file for the runner. Default locations are `./crystalball.yml` and `./config/crystalball.yml`.
10
+ Please check an [example of a config file](https://github.com/toptal/crystalball/blob/master/spec/fixtures/crystalball.yml) and [configuration defaults](https://github.com/toptal/crystalball/blob/master/lib/crystalball/rspec/runner/configuration.rb#L10) for available options.
11
+ Please keep in mind that additional generator\prediction strategies can introduce additional configuration options.
12
+
13
+ #### Overriding config file
14
+
15
+ If you want to override the path to config file please set `CRYSTALBALL_CONFIG=path/to/crystalball.yml` env variable.
16
+
17
+ Any specific configuration option in `crystalball.yml` can be overridden by providing ENV variable with "CRYSTALBALL_" prefix.
18
+ E.g. `CRYSTALBALL_EXAMPLES_LIMIT=10` will set `examples_limit` value to 10 regardless of what you have in config file.
19
+
20
+ More examples:
21
+
22
+ * `CRYSTALBALL_EXAMPLES_LIMIT=0` sets no limit on prediction size
23
+ * `CRYSTALBALL_MAP_EXPIRATION_PERIOD=0` sets no expiration period for maps
24
+ * `CRYSTALBALL_DIFF_FROM=origin/master` changes diff building to be `git diff origin/master`
data/lib/crystalball.rb CHANGED
@@ -1,14 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'crystalball/logging'
3
4
  require 'crystalball/git_repo'
5
+ require 'crystalball/extensions/git'
4
6
  require 'crystalball/rspec/prediction_builder'
5
7
  require 'crystalball/rspec/runner'
6
8
  require 'crystalball/prediction'
7
9
  require 'crystalball/predictor'
8
10
  require 'crystalball/predictor/modified_execution_paths'
9
11
  require 'crystalball/predictor/modified_specs'
12
+ require 'crystalball/predictor/modified_support_specs'
10
13
  require 'crystalball/predictor/associated_specs'
11
- require 'crystalball/case_map'
14
+ require 'crystalball/example_group_map'
12
15
  require 'crystalball/execution_map'
13
16
  require 'crystalball/map_generator'
14
17
  require 'crystalball/map_generator/configuration'
@@ -23,7 +26,7 @@ module Crystalball
23
26
  # Prints the list of specs which might fail
24
27
  #
25
28
  # @param [String] workdir - path to the root directory of repository (usually contains .git folder inside). Default: current directory
26
- # @param [String] map_path - path to the execution map. Default: execution_map.yml
29
+ # @param [String] map_path - path to the execution map. Default: crystalball_data.yml
27
30
  # @param [Proc] block - used to configure predictors
28
31
  #
29
32
  # @example
@@ -31,8 +34,10 @@ module Crystalball
31
34
  # predictor.use Crystalball::Predictor::ModifiedExecutionPaths.new
32
35
  # predictor.use Crystalball::Predictor::ModifiedSpecs.new
33
36
  # end
34
- def self.foresee(workdir: '.', map_path: 'execution_map.yml', &block)
37
+ def self.foresee(workdir: '.', map_path: 'crystalball_data.yml', &block)
35
38
  map = MapStorage::YAMLStorage.load(Pathname(map_path))
36
39
  Predictor.new(map, GitRepo.open(Pathname(workdir)), from: map.commit, &block).prediction.compact
37
40
  end
41
+
42
+ extend Logging
38
43
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'crystalball/rails/tables_map_generator'
4
+ require 'crystalball/rails/predictor/modified_schema'
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crystalball
4
+ # Data object to store execution map for specific example
5
+ class ExampleGroupMap
6
+ attr_reader :uid, :file_path, :used_files
7
+ extend Forwardable
8
+
9
+ delegate %i[push each] => :used_files
10
+
11
+ # @param [Example|ExampleGroup] example - RSpec example or example group
12
+ # @param [Array<String>] used_files - list of files affected by example
13
+ def initialize(example, used_files = [])
14
+ @uid = example.id
15
+ @file_path = example.file_path
16
+ @used_files = used_files
17
+ end
18
+ end
19
+ end
@@ -7,49 +7,50 @@ module Crystalball
7
7
 
8
8
  # Simple data object for map metadata information
9
9
  class Metadata
10
- attr_accessor :commit, :type, :version
10
+ attr_reader :commit, :type, :version, :timestamp
11
11
 
12
12
  # @param [String] commit - SHA of commit
13
13
  # @param [String] type - type of execution map
14
14
  # @param [Numeric] version - map generator version number
15
- def initialize(commit: nil, type: nil, version: nil)
15
+ def initialize(commit: nil, type: nil, version: nil, timestamp: nil)
16
16
  @commit = commit
17
17
  @type = type
18
+ @timestamp = timestamp
18
19
  @version = version
19
20
  end
20
21
 
21
22
  def to_h
22
- {type: type, commit: commit, version: version}
23
+ {type: type, commit: commit, timestamp: timestamp, version: version}
23
24
  end
24
25
  end
25
26
 
26
- attr_reader :cases, :metadata
27
+ attr_reader :example_groups, :metadata
27
28
 
28
- delegate %i[commit commit= version version=] => :metadata
29
- delegate %i[size] => :cases
29
+ delegate %i[commit version timestamp] => :metadata
30
+ delegate %i[size] => :example_groups
30
31
 
31
32
  # @param [Hash] metadata - add or override metadata of execution map
32
- # @param [Hash] cases - initial list of cases
33
- def initialize(metadata: {}, cases: {})
34
- @cases = cases
33
+ # @param [Hash] example_groups - initial list of example groups data
34
+ def initialize(metadata: {}, example_groups: {})
35
+ @example_groups = example_groups
35
36
 
36
37
  @metadata = Metadata.new(type: self.class.name, **metadata)
37
38
  end
38
39
 
39
- # Adds case map to the list
40
+ # Adds example group map to the list
40
41
  #
41
- # @param [Crystalball::CaseMap] case_map
42
- def <<(case_map)
43
- cases[case_map.uid] = case_map.affected_files.uniq
42
+ # @param [Crystalball::ExampleGroupMap] example_group_map
43
+ def <<(example_group_map)
44
+ example_groups[example_group_map.uid] = example_group_map.used_files.uniq
44
45
  end
45
46
 
46
- # Remove all cases
47
+ # Remove all example_groups
47
48
  def clear!
48
- self.cases = {}
49
+ self.example_groups = {}
49
50
  end
50
51
 
51
52
  private
52
53
 
53
- attr_writer :cases, :metadata
54
+ attr_writer :example_groups, :metadata
54
55
  end
55
56
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'crystalball/extensions/git/base'
4
+ require 'crystalball/extensions/git/lib'
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Represents git repo object itself.
5
+ class Base
6
+ # `git merge-base ...`. Returns common ancestor for all passed commits
7
+ #
8
+ # @param [Array<Object>] args - list of commits to process. Last argument can be options for merge-base command
9
+ # @return [Git::Object::Commit]
10
+ def merge_base(*args)
11
+ gcommit(lib.merge_base(*args))
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ # Class wich holds whole collection of raw methods to work with git
5
+ class Lib
6
+ # `git merge-base ...`. Returns common ancestor for all passed commits
7
+ #
8
+ # @param [Array<Object>] args - list of commits to process. Last argument can be options for merge-base command
9
+ # @return [String]
10
+ def merge_base(*args)
11
+ opts = args.last.is_a?(Hash) ? args.pop : {}
12
+
13
+ arg_opts = opts.map { |k, v| "--#{k}" if v }.compact + args
14
+
15
+ command('merge-base', arg_opts)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'crystalball/map_generator/factory_bot_strategy'
@@ -26,18 +26,13 @@ module Crystalball
26
26
  @repo_path = repo_path
27
27
  end
28
28
 
29
- # Check if repository has no uncommitted changes
30
- def pristine?
31
- diff.empty?
32
- end
33
-
34
29
  # Proxy all unknown calls to `Git` object
35
30
  def method_missing(method, *args, &block)
36
- repo.public_send(method, *args, &block) || super
31
+ repo.public_send(method, *args, &block)
37
32
  end
38
33
 
39
34
  def respond_to_missing?(method, *)
40
- repo.respond_to?(method, false) || super
35
+ repo.respond_to?(method, false)
41
36
  end
42
37
 
43
38
  # Creates diff
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Crystalball
6
+ # This module logs information to the standard output based on the configured log level,
7
+ # and also logs unfiltered information to the configured log file.
8
+ module Logging
9
+ def log(severity_sym, *args, &block)
10
+ output_stream.log(severity(severity_sym), *args, &block)
11
+ log_file_output_stream.log(severity(severity_sym), *args, &block)
12
+ end
13
+
14
+ def self.extended(base)
15
+ base.private_class_method :severity, :output_stream, :log_file_output_stream, :configured_level, :config
16
+ end
17
+
18
+ # @api private
19
+ def reset_logger
20
+ @output_stream = nil
21
+ @log_file_output_stream = nil
22
+ end
23
+
24
+ def severity(severity_sym)
25
+ ::Logger.const_get(severity_sym.to_s.upcase)
26
+ end
27
+
28
+ def output_stream
29
+ @output_stream ||= ::Logger.new(STDOUT).tap do |logger|
30
+ logger.level = severity(configured_level)
31
+ end
32
+ end
33
+
34
+ def log_file_output_stream
35
+ @log_file_output_stream ||= begin
36
+ config['log_file'].dirname.mkpath
37
+ ::Logger.new(config['log_file']).tap do |logger|
38
+ logger.level = ::Logger::DEBUG
39
+ end
40
+ end
41
+ end
42
+
43
+ def configured_level
44
+ config['log_level'].to_sym
45
+ end
46
+
47
+ def config
48
+ @config ||= Crystalball::RSpec::Runner.config
49
+ end
50
+ end
51
+ end
@@ -27,14 +27,12 @@ module Crystalball
27
27
 
28
28
  def initialize
29
29
  @configuration = Configuration.new
30
- @configuration.commit = repo.object('HEAD').sha if repo
30
+ @configuration.commit = repo.gcommit('HEAD') if repo
31
31
  yield @configuration if block_given?
32
32
  end
33
33
 
34
34
  # Registers strategies and prepares metadata for execution map
35
35
  def start!
36
- raise 'Repository is not pristine! Please stash all your changes' if repo && !repo.pristine?
37
-
38
36
  self.map = nil
39
37
  map_storage.clear!
40
38
  map_storage.dump(map.metadata.to_h)
@@ -45,7 +43,7 @@ module Crystalball
45
43
 
46
44
  # Runs example and collects execution map for it
47
45
  def refresh_for_case(example)
48
- map << strategies.run(CaseMap.new(example), example) { example.run }
46
+ map << strategies.run(ExampleGroupMap.new(example), example) { example.run }
49
47
  check_dump_threshold
50
48
  end
51
49
 
@@ -54,11 +52,11 @@ module Crystalball
54
52
  return unless started
55
53
 
56
54
  strategies.each(&:before_finalize)
57
- map_storage.dump(map.cases) if map.size.positive?
55
+ map_storage.dump(map.example_groups) if map.size.positive?
58
56
  end
59
57
 
60
58
  def map
61
- @map ||= map_class.new(metadata: {commit: configuration.commit, version: configuration.version})
59
+ @map ||= map_class.new(metadata: {commit: configuration.commit&.sha, timestamp: configuration.commit&.date&.to_i, version: configuration.version})
62
60
  end
63
61
 
64
62
  private
@@ -74,7 +72,7 @@ module Crystalball
74
72
  def check_dump_threshold
75
73
  return unless dump_threshold.positive? && map.size >= dump_threshold
76
74
 
77
- map_storage.dump(map.cases)
75
+ map_storage.dump(map.example_groups)
78
76
  map.clear!
79
77
  end
80
78
  end
@@ -30,14 +30,15 @@ module Crystalball
30
30
  @execution_detector = execution_detector
31
31
  end
32
32
 
33
- # Adds to the affected files every file which contain the definition of the
33
+ # Adds to the used files every file which contain the definition of the
34
34
  # classes of the objects allocated during the spec execution.
35
- # @param [Crystalball::CaseMap] case_map - object holding example metadata and affected files
36
- def call(case_map, example)
35
+ # @param [Crystalball::ExampleGroupMap] example_map - object holding example metadata and used files
36
+ # @param [RSpec::Core::Example] example - a RSpec example
37
+ def call(example_map, example)
37
38
  classes = object_tracker.used_classes_during do
38
- yield case_map, example
39
+ yield example_map, example
39
40
  end
40
- case_map.push(*execution_detector.detect(classes))
41
+ example_map.push(*execution_detector.detect(classes))
41
42
  end
42
43
  end
43
44
  end
@@ -35,6 +35,7 @@ module Crystalball
35
35
  @trace_point ||= TracePoint.new(:c_call) do |tp|
36
36
  next unless tp.method_id == :new || tp.method_id == :allocate
37
37
  next unless whitelisted_constants.any? { |c| tp.self <= c }
38
+
38
39
  created_object_classes << tp.self
39
40
  end
40
41
  end
@@ -10,10 +10,11 @@ module Crystalball
10
10
 
11
11
  def before_finalize; end
12
12
 
13
- # Each strategy must implement #call augmenting the affected_files list and
14
- # yielding back the CaseMap.
15
- # @param [Crystalball::CaseMap] _case_map - object holding example metadata and affected files
16
- def call(_case_map, _example)
13
+ # Each strategy must implement #call augmenting the used_files list and
14
+ # yielding back the ExampleGroupMap.
15
+ # @param [Crystalball::ExampleGroupMap] _example_map - object holding example metadata and used files
16
+ # @param [RSpec::Core::Example] _example - a RSpec example
17
+ def call(_example_map, _example)
17
18
  raise NotImplementedError
18
19
  end
19
20
  end
@@ -23,7 +23,7 @@ module Crystalball
23
23
  end
24
24
 
25
25
  def map_storage_path
26
- @map_storage_path ||= Pathname('execution_map.yml')
26
+ @map_storage_path ||= Pathname('tmp/crystalball_data.yml')
27
27
  end
28
28
 
29
29
  def map_storage_path=(value)
@@ -20,14 +20,15 @@ module Crystalball
20
20
  Coverage.start
21
21
  end
22
22
 
23
- # Adds to the case_map's affected files the ones the ones in which
23
+ # Adds to the example_map's used files the ones the ones in which
24
24
  # the coverage has changed after the tests runs.
25
- # @param [Crystalball::CaseMap] case_map - object holding example metadata and affected files
26
- def call(case_map, _)
25
+ # @param [Crystalball::ExampleGroupMap] example_map - object holding example metadata and used files
26
+ # @param [RSpec::Core::Example] example - a RSpec example
27
+ def call(example_map, example)
27
28
  before = Coverage.peek_result
28
- yield case_map
29
+ yield example_map, example
29
30
  after = Coverage.peek_result
30
- case_map.push(*execution_detector.detect(before, after))
31
+ example_map.push(*execution_detector.detect(before, after))
31
32
  end
32
33
  end
33
34
  end
@@ -21,14 +21,14 @@ module Crystalball
21
21
  @execution_detector = execution_detector
22
22
  end
23
23
 
24
- # @param [Crystalball::CaseMap] case_map - object holding example metadata and affected files
25
- # @param [RSpec::Core::Example] example
26
- def call(case_map, example)
27
- yield case_map
24
+ # @param [Crystalball::ExampleGroupMap] example_map - object holding example metadata and used files
25
+ # @param [RSpec::Core::Example] example - a RSpec example
26
+ def call(example_map, example)
27
+ yield example_map, example
28
28
 
29
29
  described_class = example.metadata[:described_class]
30
30
 
31
- case_map.push(*execution_detector.detect([described_class])) if described_class
31
+ example_map.push(*execution_detector.detect([described_class])) if described_class
32
32
  end
33
33
  end
34
34
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'crystalball/map_generator/factory_bot_strategy/factory_gem_loader'
4
+
5
+ Crystalball::MapGenerator::FactoryBotStrategy::FactoryGemLoader.require!
6
+
7
+ require 'crystalball/map_generator/base_strategy'
8
+ require 'crystalball/map_generator/helpers/path_filter'
9
+ require 'crystalball/map_generator/factory_bot_strategy/dsl_patch'
10
+ require 'crystalball/map_generator/factory_bot_strategy/factory_runner_patch'
11
+
12
+ module Crystalball
13
+ class MapGenerator
14
+ # Map generator strategy to include list of strategies which was used in an example.
15
+ class FactoryBotStrategy
16
+ include ::Crystalball::MapGenerator::BaseStrategy
17
+ include ::Crystalball::MapGenerator::Helpers::PathFilter
18
+
19
+ class << self
20
+ def factory_bot_constant
21
+ defined?(::FactoryBot) ? ::FactoryBot : ::FactoryGirl
22
+ end
23
+
24
+ # List of factories used by current example
25
+ #
26
+ # @return [Array<String>]
27
+ def used_factories
28
+ @used_factories ||= []
29
+ end
30
+
31
+ # Map of factories to files
32
+ #
33
+ # @return [Hash<String, String>]
34
+ def factory_definitions
35
+ @factory_definitions ||= {}
36
+ end
37
+
38
+ # Reset cached list of factories
39
+ def reset_used_factories
40
+ @used_factories = []
41
+ end
42
+ end
43
+
44
+ def after_register
45
+ DSLPatch.apply!
46
+ FactoryRunnerPatch.apply!
47
+ end
48
+
49
+ # Adds factories related to the spec to the map
50
+ # @param [Crystalball::ExampleGroupMap] example_map - object holding example metadata and used files
51
+ # @param [RSpec::Core::Example] example - a RSpec example
52
+ def call(example_map, example)
53
+ self.class.reset_used_factories
54
+ yield example_map, example
55
+ example_map.push(*filter(self.class.used_factories.flat_map { |f| self.class.factory_definitions[f] }))
56
+ end
57
+ end
58
+ end
59
+ end