shrek 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -1
  3. data/examples/tree_pruning/.gitignore +30 -0
  4. data/examples/tree_pruning/.rspec +3 -0
  5. data/examples/tree_pruning/.rubocop.yml +10 -0
  6. data/examples/tree_pruning/.ruby-version +1 -0
  7. data/examples/tree_pruning/Gemfile +70 -0
  8. data/examples/tree_pruning/README.md +83 -0
  9. data/examples/tree_pruning/Rakefile +8 -0
  10. data/examples/tree_pruning/app/assets/config/manifest.js +3 -0
  11. data/examples/tree_pruning/app/assets/images/.keep +0 -0
  12. data/examples/tree_pruning/app/assets/javascripts/application.js +16 -0
  13. data/examples/tree_pruning/app/assets/javascripts/cable.js +13 -0
  14. data/examples/tree_pruning/app/assets/javascripts/channels/.keep +0 -0
  15. data/examples/tree_pruning/app/assets/stylesheets/application.css +15 -0
  16. data/examples/tree_pruning/app/channels/application_cable/channel.rb +6 -0
  17. data/examples/tree_pruning/app/channels/application_cable/connection.rb +6 -0
  18. data/examples/tree_pruning/app/controllers/application_controller.rb +4 -0
  19. data/examples/tree_pruning/app/controllers/concerns/.keep +0 -0
  20. data/examples/tree_pruning/app/controllers/tree_controller.rb +13 -0
  21. data/examples/tree_pruning/app/helpers/application_helper.rb +4 -0
  22. data/examples/tree_pruning/app/jobs/application_job.rb +4 -0
  23. data/examples/tree_pruning/app/lib/layers/external_fetcher.rb +21 -0
  24. data/examples/tree_pruning/app/lib/layers/parser.rb +11 -0
  25. data/examples/tree_pruning/app/lib/layers/selector.rb +46 -0
  26. data/examples/tree_pruning/app/lib/statistics_processor.rb +9 -0
  27. data/examples/tree_pruning/app/mailers/application_mailer.rb +6 -0
  28. data/examples/tree_pruning/app/models/application_record.rb +5 -0
  29. data/examples/tree_pruning/app/models/concerns/.keep +0 -0
  30. data/examples/tree_pruning/app/views/layouts/application.html.erb +15 -0
  31. data/examples/tree_pruning/app/views/layouts/mailer.html.erb +13 -0
  32. data/examples/tree_pruning/app/views/layouts/mailer.text.erb +1 -0
  33. data/examples/tree_pruning/bin/bundle +5 -0
  34. data/examples/tree_pruning/bin/rails +11 -0
  35. data/examples/tree_pruning/bin/rake +11 -0
  36. data/examples/tree_pruning/bin/setup +38 -0
  37. data/examples/tree_pruning/bin/spring +18 -0
  38. data/examples/tree_pruning/bin/update +33 -0
  39. data/examples/tree_pruning/bin/yarn +11 -0
  40. data/examples/tree_pruning/config.ru +7 -0
  41. data/examples/tree_pruning/config/application.rb +21 -0
  42. data/examples/tree_pruning/config/boot.rb +6 -0
  43. data/examples/tree_pruning/config/cable.yml +10 -0
  44. data/examples/tree_pruning/config/credentials.yml.enc +1 -0
  45. data/examples/tree_pruning/config/database.yml +25 -0
  46. data/examples/tree_pruning/config/environment.rb +7 -0
  47. data/examples/tree_pruning/config/environments/development.rb +63 -0
  48. data/examples/tree_pruning/config/environments/production.rb +96 -0
  49. data/examples/tree_pruning/config/environments/test.rb +48 -0
  50. data/examples/tree_pruning/config/initializers/application_controller_renderer.rb +9 -0
  51. data/examples/tree_pruning/config/initializers/assets.rb +16 -0
  52. data/examples/tree_pruning/config/initializers/backtrace_silencers.rb +8 -0
  53. data/examples/tree_pruning/config/initializers/content_security_policy.rb +26 -0
  54. data/examples/tree_pruning/config/initializers/cookies_serializer.rb +7 -0
  55. data/examples/tree_pruning/config/initializers/filter_parameter_logging.rb +6 -0
  56. data/examples/tree_pruning/config/initializers/inflections.rb +17 -0
  57. data/examples/tree_pruning/config/initializers/mime_types.rb +5 -0
  58. data/examples/tree_pruning/config/initializers/wrap_parameters.rb +16 -0
  59. data/examples/tree_pruning/config/locales/en.yml +33 -0
  60. data/examples/tree_pruning/config/puma.rb +36 -0
  61. data/examples/tree_pruning/config/routes.rb +6 -0
  62. data/examples/tree_pruning/config/spring.rb +8 -0
  63. data/examples/tree_pruning/config/storage.yml +34 -0
  64. data/examples/tree_pruning/db/seeds.rb +8 -0
  65. data/examples/tree_pruning/lib/assets/.keep +0 -0
  66. data/examples/tree_pruning/lib/tasks/.keep +0 -0
  67. data/examples/tree_pruning/log/.keep +0 -0
  68. data/examples/tree_pruning/package.json +5 -0
  69. data/examples/tree_pruning/public/404.html +67 -0
  70. data/examples/tree_pruning/public/422.html +67 -0
  71. data/examples/tree_pruning/public/500.html +66 -0
  72. data/examples/tree_pruning/public/apple-touch-icon-precomposed.png +0 -0
  73. data/examples/tree_pruning/public/apple-touch-icon.png +0 -0
  74. data/examples/tree_pruning/public/favicon.ico +0 -0
  75. data/examples/tree_pruning/public/robots.txt +1 -0
  76. data/examples/tree_pruning/spec/fixtures/.keep +0 -0
  77. data/examples/tree_pruning/spec/fixtures/files/.keep +0 -0
  78. data/examples/tree_pruning/spec/fixtures/files/people_stats.json +2682 -0
  79. data/examples/tree_pruning/spec/lib/layers/external_fetcher_spec.rb +32 -0
  80. data/examples/tree_pruning/spec/lib/layers/parser_spec.rb +17 -0
  81. data/examples/tree_pruning/spec/lib/layers/selector_spec.rb +40 -0
  82. data/examples/tree_pruning/spec/lib/statistics_processor_spec.rb +24 -0
  83. data/examples/tree_pruning/spec/rails_helper.rb +59 -0
  84. data/examples/tree_pruning/spec/spec_helper.rb +96 -0
  85. data/examples/tree_pruning/spec/support/people_stats.rb +15 -0
  86. data/examples/tree_pruning/spec/system/root_spec.rb +30 -0
  87. data/examples/tree_pruning/tmp/.keep +0 -0
  88. data/examples/tree_pruning/vendor/.keep +0 -0
  89. data/lib/shrek/runner.rb +5 -1
  90. data/lib/shrek/version.rb +1 -1
  91. metadata +88 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1e94b489a95faebdcfb659d0d05cfff09f61c06db8a045879cab5069ec6e81b
4
- data.tar.gz: 5322185041b996e16547cf01db847d45e9ba10acc8dee6c2765a2c5808e0c026
3
+ metadata.gz: '068de42c241b85ca45f4a3b3221c34e706789b5a73ac7b98734dc1d84610fec2'
4
+ data.tar.gz: 054b17a00cef2897177cfd0a1eda89a3a6adef6ef10b6f2fd58da1b4e4f65b52
5
5
  SHA512:
6
- metadata.gz: 908cf284a79067aba046d1c9e078931ac9a7223d87605c1ea6753b5bf7be545225169fdff4865d6002f090ee0bba24f2c8287958374fc66b207b1988defd8d6a
7
- data.tar.gz: f9257be0e9413dcd823caf44a4b4890b3801e147c98389dc3c94831742559a601c9d3b4a40fbdbe383b50f2f6d4f154892cfe0ae2da38a71e2a919ea33f66b73
6
+ metadata.gz: 94817e382d811bb64006470143a8f267c9aa91bf2fd1080c24b4c2e0d310c585b419b9b68430d827371e524ee5c51087e82495a7bc88d9fa7bd9685876283819
7
+ data.tar.gz: e492291ac0d33d772bfe61f8f2219f46643939a70ddecedf52b7ff03379f18861406431b4d6218913df68373acc3f369aad5768c473ce99f24bcd1aebdf5cf32
data/README.md CHANGED
@@ -68,9 +68,13 @@ may be used in own classes
68
68
  include Shrek
69
69
  end
70
70
 
71
- Paint.new.use_layers FindPaints, SelectWall, PrepareBrushes
71
+ Paint.new.use_layers FindPaints, SelectWall, PrepareBrushes,
72
+ self_return: ->(result) { a }
72
73
  ```
73
74
 
75
+ We also are able to make some tuning with returning value with `self_return` option.
76
+ It use something, which respond_to :call.
77
+
74
78
  Let's summarize procs and cons of this concept:
75
79
 
76
80
  ##### Procs
@@ -0,0 +1,30 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore the default SQLite database.
11
+ /db/*.sqlite3
12
+ /db/*.sqlite3-journal
13
+
14
+ # Ignore all logfiles and tempfiles.
15
+ /log/*
16
+ /tmp/*
17
+ !/log/.keep
18
+ !/tmp/.keep
19
+
20
+ # Ignore uploaded files in development
21
+ /storage/*
22
+
23
+ /node_modules
24
+ /yarn-error.log
25
+
26
+ /public/assets
27
+ .byebug_history
28
+
29
+ # Ignore master key for decrypting credentials and more.
30
+ /config/master.key
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,10 @@
1
+ inherit_from: ../../.rubocop.yml
2
+
3
+ Style/MixinUsage:
4
+ Enabled: false
5
+
6
+ Style/ClassAndModuleChildren:
7
+ Enabled: false
8
+
9
+ Metrics/AbcSize:
10
+ Max: 20
@@ -0,0 +1 @@
1
+ 2.5.1
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5
+
6
+ ruby '2.5.1'
7
+
8
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
9
+ gem 'rails', '~> 5.2.0'
10
+ # Use sqlite3 as the database for Active Record
11
+ gem 'sqlite3'
12
+ # Use Puma as the app server
13
+ gem 'puma', '~> 3.11'
14
+ # Use SCSS for stylesheets
15
+ gem 'sass-rails', '~> 5.0'
16
+ # Use Uglifier as compressor for JavaScript assets
17
+ gem 'uglifier', '>= 1.3.0'
18
+ # See https://github.com/rails/execjs#readme for more supported runtimes
19
+ # gem 'mini_racer', platforms: :ruby
20
+
21
+ # Use CoffeeScript for .coffee assets and views
22
+ gem 'coffee-rails', '~> 4.2'
23
+ # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
24
+ gem 'turbolinks', '~> 5'
25
+ # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
26
+ gem 'jbuilder', '~> 2.5'
27
+ # Use Redis adapter to run Action Cable in production
28
+ # gem 'redis', '~> 4.0'
29
+ # Use ActiveModel has_secure_password
30
+ # gem 'bcrypt', '~> 3.1.7'
31
+
32
+ # Use ActiveStorage variant
33
+ # gem 'mini_magick', '~> 4.8'
34
+
35
+ # Use Capistrano for deployment
36
+ # gem 'capistrano-rails', group: :development
37
+
38
+ # Reduces boot times through caching; required in config/boot.rb
39
+ gem 'bootsnap', '>= 1.1.0', require: false
40
+
41
+ gem 'ice_nine', '~> 0.11'
42
+ gem 'shrek', '~> 0.2'
43
+
44
+ group :development, :test do
45
+ # Call 'byebug' anywhere in the code to stop execution and get a debugger console
46
+ gem 'byebug', platforms: %i[mri mingw x64_mingw]
47
+ gem 'pry-rails', '~> 0.3'
48
+ gem 'rspec-rails', '~> 3.7'
49
+ end
50
+
51
+ group :development do
52
+ # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
53
+ gem 'listen', '>= 3.0.5', '< 3.2'
54
+ gem 'web-console', '>= 3.3.0'
55
+ # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
56
+ gem 'spring'
57
+ gem 'spring-watcher-listen', '~> 2.0.0'
58
+ end
59
+
60
+ group :test do
61
+ # Adds support for Capybara system testing and selenium driver
62
+ gem 'capybara', '>= 2.15', '< 4.0'
63
+ gem 'selenium-webdriver'
64
+ # Easy installation and use of chromedriver to run system tests with Chrome
65
+ gem 'chromedriver-helper'
66
+ gem 'webmock', '~> 3.3'
67
+ end
68
+
69
+ # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
70
+ gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
@@ -0,0 +1,83 @@
1
+ # The problem
2
+
3
+ Imagine you have a tree of various Indicators that track something about a country's population. The client wants to display them, but only a few of them at a time.
4
+
5
+ These Indicators are part of a tree, and they’re grouped by Themes, Sub-themes, and Categories. For example, we have an indicator called “Crude death rate”. It resides in the Category "Births per year", which lives inside the Sub-theme “Births and Deaths”, which in turn lives inside the Theme “Demographics”.
6
+
7
+ This data comes from a client-provided service, which doesn’t support picking and choosing which things to filter. Because of valid reasons we won’t expound on here, it wouldn’t make sense to add this feature to the upstream service, and it’s on us to build an intermediary service that can do this.
8
+
9
+
10
+ Because we don’t know how to program, this service explodes quite regularly. It’s expected that your solution should retry a few times in case of failure.
11
+
12
+ You can find sample input data in the test suite
13
+
14
+ ## Solution
15
+
16
+ You’re to make an API that has one single endpoint, /tree/<name>. It will receive the name of the upstream tree to request, as well as a list of values as GET params, indicator_ids[] to filter for. If you get asked to show an Indicator, you should return the subtree that reaches it. Here's an example URL:
17
+
18
+ http://127.0.0.1:5000/tree/input?indicator_ids[]=31&indicator_ids[]=32&indicator_ids[]=1
19
+
20
+ Except for filtering out uninteresting Categories, Sub-themes and Themes, you must preserve all the other attributes in the nodes.
21
+
22
+ ## Potential edge cases
23
+
24
+ After an earnest try to maintain service, if the upstream still doesn’t work, return a 500.
25
+ If the tree you were asked for does not exist, return a 404.
26
+
27
+ The upstream server has no schedule for changes. Don’t cache its answers.
28
+
29
+ ## Things that we’ll pay attention to
30
+
31
+ * Code ergonomy (style and readability)
32
+ * Testing strategy
33
+ * Resilience to network/upstream problems
34
+ * Separation of concerns and code structure
35
+
36
+ ## Visualization of a pruned tree
37
+
38
+ In the tree below, when asked for the four indicators in bold (Urban, Male, Female, >30y), you should return a subtree containing only the highlighted nodes.
39
+
40
+ | Theme | Sub-Theme | Category | Indicator |
41
+ |------------- |---------------- |---------------- |-----------------|
42
+ | `Urban` | `Administrative` | `Area` | Rural |
43
+ | `Urban` | `Administrative` | `Area` | **Urban** |
44
+ | `Urban` | `Administrative` | Population | Total |
45
+ | `Urban` | `Administrative` | Population | Male |
46
+ | `Urban` | `Administrative` | Population | Female |
47
+ | Jobs | Labor force | Working population | Total |
48
+ | Jobs | Unemployment | 15-24 years | Male |
49
+ | Jobs | Unemployment | 15-24 years | Female |
50
+ | `Demographics` | `Birth and deaths` | Deaths per year | Natural |
51
+ | `Demographics` | `Birth and deaths` | Deaths per year | Accidents? |
52
+ | `Demographics` | `Birth and deaths` | Deaths per year | Murder |
53
+ | `Demographics` | `Birth and deaths` | `Births per year` | **Male** |
54
+ | `Demographics` | `Birth and deaths` | `Births per year` | **Female** |
55
+ | `Demographics` | `Age and sex` | `Age distribution` | < 3y |
56
+ | `Demographics` | `Age and sex` | `Age distribution` | <18y |
57
+ | `Demographics` | `Age and sex` | `Age distribution` | <30y |
58
+ | `Demographics` | `Age and sex` | `Age distribution` | **>30y** |
59
+ | `Demographics` | `Age and sex` | Sex | Yes |
60
+ | `Demographics` | `Age and sex` | Sex | No |
61
+
62
+
63
+ The resulting (pruned) tree should be this:
64
+
65
+ | Theme | Sub-Theme | Category | Indicator |
66
+ |------------- |---------------- |---------------- |-----------------|
67
+ | `Urban` | `Administrative` | `Area` | **Urban** |
68
+ | `Demographics` | `Birth and deaths` | `Births per year` | **Male** |
69
+ | `Demographics` | `Birth and deaths` | `Births per year` | **Female** |
70
+ | `Demographics` | `Age and sex` | `Age distribution` | **>30y** |
71
+
72
+ # Result
73
+
74
+ Finish endpoint can used with
75
+
76
+ ```
77
+ curl -v 'http://127.0.0.1:3000/?indicator_ids\[\]=31&indicator_ids\[\]=32&indicator_ids\[\]=1'
78
+ ```
79
+
80
+ In the real world will be good to move all exceptions handling to `ApplicationController`
81
+ but now I think it is ok as it is.
82
+
83
+ I still do not have real endpoint. Use stub file is not right, so I leaved stub uri.
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
4
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
5
+
6
+ require_relative 'config/application'
7
+
8
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../javascripts .js
3
+ //= link_directory ../stylesheets .css
File without changes
@@ -0,0 +1,16 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
5
+ // vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require rails-ujs
14
+ //= require activestorage
15
+ //= require turbolinks
16
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ // Action Cable provides the framework to deal with WebSockets in Rails.
2
+ // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3
+ //
4
+ //= require action_cable
5
+ //= require_self
6
+ //= require_tree ./channels
7
+
8
+ (function() {
9
+ this.App || (this.App = {});
10
+
11
+ App.cable = ActionCable.createConsumer();
12
+
13
+ }).call(this);
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6
+ * vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApplicationCable
4
+ class Channel < ActionCable::Channel::Base
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApplicationCable
4
+ class Connection < ActionCable::Connection::Base
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationController < ActionController::Base
4
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TreeController < ApplicationController
4
+ def index
5
+ render StatisticsProcessor.new.call(permitted_params.to_h.symbolize_keys)
6
+ end
7
+
8
+ private
9
+
10
+ def permitted_params
11
+ params.permit(sub_theme_ids: [], categorie_ids: [], indicator_ids: [])
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApplicationHelper
4
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationJob < ActiveJob::Base
4
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ module Layers
5
+ class ExternalFetcher < Shrek::Layers
6
+ def call(uri: 'enter_real_uri_here.stub', path: '/', tries: 3, **options)
7
+ next_layer.call(response(uri, path), options)
8
+ rescue StandardError => e
9
+ tries -= 1
10
+ retry if tries.positive?
11
+ { json: e.message, status: :internal_server_error }
12
+ end
13
+
14
+ private
15
+
16
+ def response(uri, path)
17
+ Net::HTTP.get_response(uri, path).body
18
+ # open(Rails.root.join('spec', 'fixtures', 'files', 'people_stats.json')).read
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Layers
4
+ class Parser < Shrek::Layers
5
+ def call(body = '', **options)
6
+ next_layer.call JSON.parse(body), options
7
+ rescue JSON::ParserError => e
8
+ { json: e.message, status: :unprocessable_entity }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Layers
4
+ module NestedHashFilter
5
+ refine Hash do
6
+ def fetch_for(wanted_key, wanted_ids)
7
+ fetch(wanted_key.to_s, []).delete_if do |wanted|
8
+ next if Array(wanted_ids).map(&:to_i).include? wanted.fetch('id') { raise JSON::ParserError }
9
+ block_given? ? yield(wanted) : true
10
+ end.empty?
11
+ end
12
+
13
+ def proceed(**args)
14
+ return fetch_for(*args.shift) if args.keys.one?
15
+ fetch_for(*args.shift) { |wrap| wrap.proceed(args) if args.any? }
16
+ end
17
+ end
18
+ end
19
+
20
+ using Layers::NestedHashFilter
21
+
22
+ class NotFound < StandardError; end
23
+
24
+ class Selector < Shrek::Layers
25
+ def call(data, sub_theme_ids: nil, categorie_ids: nil, indicator_ids: nil, **_options)
26
+ return { json: data } if Array(sub_theme_ids).empty? &&
27
+ Array(categorie_ids).empty? &&
28
+ Array(indicator_ids).empty? && data.any?
29
+
30
+ result = data.delete_if do |w|
31
+ w.proceed(sub_themes: sub_theme_ids, categories: categorie_ids, indicators: indicator_ids)
32
+ end
33
+
34
+ raise(NotFound) if result.empty?
35
+ { json: result }
36
+ rescue NotFound
37
+ not_found
38
+ end
39
+
40
+ private
41
+
42
+ def not_found
43
+ { json: '', status: :not_found }
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StatisticsProcessor
4
+ include Shrek
5
+
6
+ def call(*args)
7
+ use_layers(::Layers::ExternalFetcher, ::Layers::Parser, ::Layers::Selector).call(*args)
8
+ end
9
+ end