active_element 0.0.1 → 0.0.3

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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -0
  3. data/.strong_versions.yml +2 -0
  4. data/Gemfile +10 -2
  5. data/Gemfile.lock +229 -4
  6. data/Rakefile +1 -0
  7. data/active_element.gemspec +7 -0
  8. data/app/assets/config/active_element/manifest.js +2 -0
  9. data/app/assets/javascripts/active_element/application.js +10 -0
  10. data/app/assets/javascripts/active_element/confirm.js +67 -0
  11. data/app/assets/javascripts/active_element/form.js +61 -0
  12. data/app/assets/javascripts/active_element/json_field.js +316 -0
  13. data/app/assets/javascripts/active_element/pagination.js +18 -0
  14. data/app/assets/javascripts/active_element/search_field.js +127 -0
  15. data/app/assets/javascripts/active_element/secret.js +40 -0
  16. data/app/assets/javascripts/active_element/setup.js +36 -0
  17. data/app/assets/javascripts/active_element/theme.js +42 -0
  18. data/app/assets/stylesheets/active_element/_variables.scss +142 -0
  19. data/app/assets/stylesheets/active_element/application.scss +77 -0
  20. data/app/controllers/active_element/application_controller.rb +41 -0
  21. data/app/controllers/active_element/text_searches_controller.rb +189 -0
  22. data/app/views/active_element/components/_horizontal_tabs.html.erb +32 -0
  23. data/app/views/active_element/components/_vertical_tabs.html.erb +38 -0
  24. data/app/views/active_element/components/button.html.erb +27 -0
  25. data/app/views/active_element/components/fields/_boolean.html.erb +11 -0
  26. data/app/views/active_element/components/form/_check_box.html.erb +3 -0
  27. data/app/views/active_element/components/form/_check_boxes.html.erb +33 -0
  28. data/app/views/active_element/components/form/_field.html.erb +28 -0
  29. data/app/views/active_element/components/form/_generic_field.html.erb +3 -0
  30. data/app/views/active_element/components/form/_json.html.erb +12 -0
  31. data/app/views/active_element/components/form/_label.html.erb +17 -0
  32. data/app/views/active_element/components/form/_option_groups_summary.html.erb +17 -0
  33. data/app/views/active_element/components/form/_select.html.erb +4 -0
  34. data/app/views/active_element/components/form/_summary.html.erb +40 -0
  35. data/app/views/active_element/components/form/_templates.html.erb +85 -0
  36. data/app/views/active_element/components/form/_text_area.html.erb +4 -0
  37. data/app/views/active_element/components/form/_text_search.html.erb +16 -0
  38. data/app/views/active_element/components/form.html.erb +78 -0
  39. data/app/views/active_element/components/json.html.erb +8 -0
  40. data/app/views/active_element/components/page_description.html.erb +3 -0
  41. data/app/views/active_element/components/secret/_field.html.erb +1 -0
  42. data/app/views/active_element/components/secret/_templates.html.erb +11 -0
  43. data/app/views/active_element/components/table/_collection_row.html.erb +30 -0
  44. data/app/views/active_element/components/table/_grouped_collection.html.erb +88 -0
  45. data/app/views/active_element/components/table/_pagination.html.erb +17 -0
  46. data/app/views/active_element/components/table/_ungrouped_collection.html.erb +49 -0
  47. data/app/views/active_element/components/table/collection.html.erb +39 -0
  48. data/app/views/active_element/components/table/item.html.erb +39 -0
  49. data/app/views/active_element/components/tabs.html.erb +7 -0
  50. data/app/views/active_element/decorators/_boolean.html.erb +5 -0
  51. data/app/views/active_element/decorators/_date.html.erb +3 -0
  52. data/app/views/active_element/decorators/_datetime.html.erb +3 -0
  53. data/app/views/active_element/decorators/_time.html.erb +3 -0
  54. data/app/views/active_element/forbidden.html.erb +33 -0
  55. data/app/views/active_element/main_menu/_item.html.erb +9 -0
  56. data/app/views/active_element/navbar/_menu.html.erb +30 -0
  57. data/app/views/active_element/theme/_select.html.erb +1 -0
  58. data/app/views/active_element/theme/_templates.html.erb +6 -0
  59. data/app/views/kaminari/_first_page.html.erb +3 -0
  60. data/app/views/kaminari/_gap.html.erb +3 -0
  61. data/app/views/kaminari/_last_page.html.erb +3 -0
  62. data/app/views/kaminari/_next_page.html.erb +3 -0
  63. data/app/views/kaminari/_page.html.erb +9 -0
  64. data/app/views/kaminari/_paginator.html.erb +17 -0
  65. data/app/views/kaminari/_prev_page.html.erb +3 -0
  66. data/app/views/layouts/active_element.html.erb +65 -0
  67. data/app/views/layouts/active_element_error.html.erb +40 -0
  68. data/config/routes.rb +5 -0
  69. data/lib/active_element/active_menu_link.rb +80 -0
  70. data/lib/active_element/active_record_text_search_authorization.rb +12 -0
  71. data/lib/active_element/colorized_string.rb +33 -0
  72. data/lib/active_element/component.rb +122 -0
  73. data/lib/active_element/components/button.rb +156 -0
  74. data/lib/active_element/components/collection_table.rb +118 -0
  75. data/lib/active_element/components/form.rb +210 -0
  76. data/lib/active_element/components/item_table.rb +57 -0
  77. data/lib/active_element/components/json.rb +31 -0
  78. data/lib/active_element/components/link_helpers.rb +9 -0
  79. data/lib/active_element/components/page_description.rb +28 -0
  80. data/lib/active_element/components/secret_fields.rb +15 -0
  81. data/lib/active_element/components/tab.rb +37 -0
  82. data/lib/active_element/components/tabs.rb +35 -0
  83. data/lib/active_element/components/translations.rb +18 -0
  84. data/lib/active_element/components/util/association_mapping.rb +80 -0
  85. data/lib/active_element/components/util/decorator.rb +107 -0
  86. data/lib/active_element/components/util/display_value_mapping.rb +48 -0
  87. data/lib/active_element/components/util/field_mapping.rb +144 -0
  88. data/lib/active_element/components/util/form_field_mapping.rb +104 -0
  89. data/lib/active_element/components/util/form_value_mapping.rb +49 -0
  90. data/lib/active_element/components/util/i18n.rb +66 -0
  91. data/lib/active_element/components/util/record_mapping.rb +111 -0
  92. data/lib/active_element/components/util.rb +43 -0
  93. data/lib/active_element/components.rb +20 -0
  94. data/lib/active_element/controller_action.rb +91 -0
  95. data/lib/active_element/engine.rb +26 -0
  96. data/lib/active_element/permissions_check.rb +101 -0
  97. data/lib/active_element/rails_component.rb +40 -0
  98. data/lib/active_element/route.rb +112 -0
  99. data/lib/active_element/routes.rb +62 -0
  100. data/lib/active_element/version.rb +1 -1
  101. data/lib/active_element.rb +91 -1
  102. data/lib/tasks/active_element.rake +23 -0
  103. data/rspec-documentation/dummy +1 -0
  104. data/rspec-documentation/pages/Components/Forms.md +1 -0
  105. data/rspec-documentation/pages/Components/Tables.md +47 -0
  106. data/rspec-documentation/pages/Components/Tabs.md +1 -0
  107. data/rspec-documentation/pages/Components.md +1 -0
  108. data/rspec-documentation/pages/Decorators/Inline Decorators.md +1 -0
  109. data/rspec-documentation/pages/Decorators/View Decorators.md +1 -0
  110. data/rspec-documentation/pages/Index.md +3 -0
  111. data/rspec-documentation/pages/Util/I18n.md +1 -0
  112. data/rspec-documentation/spec_helper.rb +35 -0
  113. metadata +191 -3
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveElement
4
+ # Verifies provided permissions against required permissions.
5
+ class PermissionsCheck
6
+ def initialize(required:, actual:, controller_path:, action_name:, rails_component:)
7
+ @required = required.presence || []
8
+ @actual = normalized(actual)
9
+ @controller_name = controller_path.to_s.gsub('/', '_')
10
+ @action_name = action_name.to_s
11
+ @rails_component = rails_component
12
+ raise_unprotected_route_error if applicable.empty?
13
+ end
14
+
15
+ def permitted?
16
+ rails_component.environment == 'development' || missing.blank?
17
+ end
18
+
19
+ def message
20
+ return development_environment_message if rails_component.environment == 'development'
21
+ return "User access granted for permission(s): #{applicable.join(', ')}" if permitted?
22
+
23
+ "User access forbidden. Missing user permission(s): #{missing.join(', ')}"
24
+ end
25
+
26
+ def missing
27
+ @missing ||= applicable.reject do |permission|
28
+ actual.include?(permission.to_s)
29
+ end.map(&:to_s)
30
+ end
31
+
32
+ def applicable
33
+ @applicable ||= default_permissions + required_permissions
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :required, :actual, :controller_name, :action_name, :rails_component
39
+
40
+ def development_environment_message
41
+ "Bypassed permission(s) in development environment: #{applicable.join(', ')}"
42
+ end
43
+
44
+ def default_permissions
45
+ return [] if normalized_action.nil?
46
+
47
+ ["can_#{normalized_action}_#{rails_component.application_name}_#{controller_name}"]
48
+ end
49
+
50
+ def required_permissions
51
+ @required_permissions ||= required.map do |permission, options|
52
+ next nil unless applicable?(options)
53
+
54
+ permission
55
+ end.compact
56
+ end
57
+
58
+ def normalized_action
59
+ {
60
+ index: 'list',
61
+ show: 'view',
62
+ edit: 'edit',
63
+ update: 'edit',
64
+ create: 'create',
65
+ new: 'create',
66
+ destroy: 'delete'
67
+ }.fetch(action_name.to_sym, nil)
68
+ end
69
+
70
+ def normalized(val)
71
+ return val&.map(&:to_s) if val.is_a?(Array)
72
+
73
+ [val].map(&:to_s)
74
+ end
75
+
76
+ def applicable?(options)
77
+ return true if !options.key?(:only) && !options.key?(:except)
78
+ return true if only_applicable?(options)
79
+ return false if except_applicable?(options)
80
+
81
+ false
82
+ end
83
+
84
+ def only_applicable?(options)
85
+ return false unless options.key?(:only)
86
+
87
+ normalized(options.fetch(:only)).include?(action_name)
88
+ end
89
+
90
+ def except_applicable?(options)
91
+ return false unless options.key?(:except)
92
+
93
+ normalized(options.fetch(:except)).include?(action_name)
94
+ end
95
+
96
+ def raise_unprotected_route_error
97
+ raise UnprotectedRouteError,
98
+ "#{controller_name.titleize.tr(' ', '')}##{action_name} must be protected with `permit_user`"
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveElement
4
+ # Abstraction of various Rails interfaces.
5
+ class RailsComponent
6
+ def initialize(rails)
7
+ @rails = rails
8
+ end
9
+
10
+ def routes
11
+ rails.application.routes
12
+ end
13
+
14
+ def environment
15
+ rails.env
16
+ end
17
+
18
+ def application_name
19
+ rails.application.class.module_parent.name.underscore
20
+ end
21
+
22
+ # Provides array of e.g. { path: "/admin/users", controller: "admin/users", action: "index" }
23
+ def route_paths_with_requirements
24
+ rails.application.routes.routes.map do |route|
25
+ { path: path_from_route_spec(route.path.spec) }.merge(route.requirements)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :rails
32
+
33
+ # Translates "/admin/users/:id(.:format)" into "/admin/users"
34
+ def path_from_route_spec(spec)
35
+ # FIXME: Find a more robust way of doing this ?
36
+ path = spec.to_s.gsub(/:.*/, '').gsub(/\(.*/, '')
37
+ path == '/' ? path : path.chomp
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveElement
4
+ # Abstraction of a Rails route, includes path, permitted state (based on user permissions), etc.
5
+ class Route
6
+ include Comparable
7
+
8
+ attr_reader :controller
9
+
10
+ def initialize(controller:, required_permissions:, user_permissions:, action:, rails_component:)
11
+ @controller = controller
12
+ @required_permissions = required_permissions
13
+ @user_permissions = user_permissions
14
+ @action = action
15
+ @rails_component = rails_component
16
+ end
17
+
18
+ def <=>(other)
19
+ return 0 if path.nil? && other.path.nil?
20
+ return 1 if path.nil?
21
+ return -1 if other.path.nil?
22
+
23
+ path <=> other.path
24
+ end
25
+
26
+ def permitted?
27
+ return @permitted if defined?(@permitted)
28
+
29
+ (@permitted = permitted_action?)
30
+ end
31
+
32
+ def path
33
+ @path ||= rails_path
34
+ end
35
+
36
+ def primary?
37
+ return false if rails_non_index_action?
38
+ return false unless resourceless_get_request?
39
+
40
+ true
41
+ end
42
+
43
+ def title
44
+ controller.controller_name.titleize
45
+ end
46
+
47
+ def spec
48
+ { controller: controller.controller_path, action: action.to_s }
49
+ end
50
+
51
+ def permissions
52
+ permissions_check.applicable.map(&:to_s)
53
+ end
54
+
55
+ def rails_route?
56
+ rails_application_route? || active_element_route?
57
+ end
58
+
59
+ private
60
+
61
+ attr_reader :required_permissions, :user_permissions, :action, :rails_component
62
+
63
+ def rails_application_route?
64
+ rails_component.routes.routes.map(&:requirements).any? { |requirements| match_spec?(requirements) }
65
+ end
66
+
67
+ def active_element_route?
68
+ ActiveElement::Engine.routes.routes.map(&:requirements).any? { |requirements| match_spec?(requirements) }
69
+ end
70
+
71
+ def match_spec?(requirements)
72
+ requirements.to_set.superset?(spec.to_set)
73
+ end
74
+
75
+ def rails_path
76
+ rails_component.routes.url_for(**spec, only_path: true)
77
+ rescue ActionController::UrlGenerationError
78
+ nil
79
+ end
80
+
81
+ def rails_non_index_action?
82
+ %i[show edit update new create destroy].include?(action)
83
+ end
84
+
85
+ def resourceless_get_request?
86
+ spec_set = spec.to_set
87
+ rails_component.routes.routes.find do |rails_route|
88
+ next false unless rails_route.requirements&.to_set&.superset?(spec_set)
89
+ next false unless rails_route.verb == 'GET'
90
+ next false unless rails_route.required_parts.empty?
91
+
92
+ true
93
+ end
94
+ end
95
+
96
+ def permitted_action?
97
+ permissions_check.permitted?
98
+ rescue UnprotectedRouteError
99
+ false
100
+ end
101
+
102
+ def permissions_check
103
+ @permissions_check ||= PermissionsCheck.new(
104
+ required: required_permissions,
105
+ actual: user_permissions,
106
+ controller_path: controller.controller_path,
107
+ action_name: action,
108
+ rails_component: rails_component
109
+ )
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveElement
4
+ # Provides an interface to available admin routes, used for populating a default navigation bar
5
+ # and detecting available permitted routes if the default root path is not permitted.
6
+ class Routes
7
+ include Enumerable
8
+
9
+ def initialize(rails_component:, permissions: [])
10
+ @permissions = permissions
11
+ @rails_component = rails_component
12
+ end
13
+
14
+ def permitted
15
+ @permitted ||= available_routes.select(&:permitted?)
16
+ end
17
+
18
+ def available
19
+ @available ||= available_routes
20
+ end
21
+
22
+ def alternative_routes
23
+ @alternative_routes ||= available.select(&:primary?).reject { |route| route.path == '/' }
24
+ end
25
+
26
+ def each(&block)
27
+ available.each(&block)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :permissions, :rails_component
33
+
34
+ def available_routes
35
+ @available_routes ||= descendants_with_permissions.map do |descendant, required_permissions|
36
+ descendant.public_methods(false).map do |action|
37
+ route(descendant, action, required_permissions)
38
+ end
39
+ end.flatten.compact.select(&:rails_route?).sort
40
+ end
41
+
42
+ def descendants_with_permissions
43
+ @descendants_with_permissions ||= descendants.map do |controller_class|
44
+ [controller_class.new, controller_class.active_element_permissions]
45
+ end.compact
46
+ end
47
+
48
+ def descendants
49
+ @descendants ||= ActiveElement::ApplicationController.descendants
50
+ end
51
+
52
+ def route(controller, action, required_permissions)
53
+ Route.new(
54
+ controller: controller,
55
+ action: action,
56
+ required_permissions: required_permissions,
57
+ user_permissions: permissions,
58
+ rails_component: rails_component
59
+ )
60
+ end
61
+ end
62
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveElement
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.3'
5
5
  end
@@ -1,8 +1,98 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'faraday'
4
+ require 'rouge'
5
+ require 'kaminari'
6
+ require 'sassc'
7
+ require 'bootstrap'
8
+ require 'active_record'
9
+ # require 'rspec/documentation' # FIXME: Load dynamically when running rspec documentation command.
10
+
3
11
  require_relative 'active_element/version'
12
+ require_relative 'active_element/active_record_text_search_authorization'
13
+ require_relative 'active_element/colorized_string'
14
+ require_relative 'active_element/active_menu_link'
15
+ require_relative 'active_element/permissions_check'
16
+ require_relative 'active_element/controller_action'
17
+ require_relative 'active_element/rails_component'
18
+ require_relative 'active_element/route'
19
+ require_relative 'active_element/routes'
20
+ require_relative 'active_element/component'
21
+ require_relative 'active_element/components'
22
+ require_relative 'active_element/engine'
4
23
 
24
+ # ActiveElement API Admin UI template and menu system.
5
25
  module ActiveElement
6
26
  class Error < StandardError; end
7
- # Your code goes here...
27
+ class UnprotectedRouteError < Error; end
28
+ class UnknownAttributeError < Error; end
29
+
30
+ class << self
31
+ attr_writer :application_name, :navbar_items
32
+
33
+ def application_title
34
+ @application_name || RailsComponent.new(Rails).application_name.titleize
35
+ end
36
+
37
+ def navbar_items(user)
38
+ @navbar_items || inferred_navbar_items(user)
39
+ end
40
+
41
+ def active_path_class(user:, current_navbar_item:, current_path:, controller_path:, action_name:)
42
+ if ActiveMenuLink.new(
43
+ rails_component: RailsComponent.new(Rails),
44
+ navbar_items: navbar_items(user),
45
+ current_path: current_path,
46
+ current_navbar_item: current_navbar_item,
47
+ controller_path: controller_path,
48
+ action_name: action_name
49
+ ).active?
50
+ 'active'
51
+ end
52
+ end
53
+
54
+ def json_pretty_print(json)
55
+ Components::Util.json_pretty_print(json)
56
+ end
57
+
58
+ def with_silenced_logging(&block)
59
+ return block.call unless silence_logging?
60
+
61
+ ActiveSupport::Notifications.unsubscribe 'render_template.action_view'
62
+ ActiveSupport::Notifications.unsubscribe 'render_partial.action_view'
63
+
64
+ block.call
65
+ end
66
+
67
+ def silence_logging?
68
+ return true unless Rails.env.development? || Rails.env.test?
69
+ return true unless ENV.key?('ACTIVE_ELEMENT_DEBUG')
70
+
71
+ false
72
+ end
73
+
74
+ def eager_load_controllers
75
+ Pathname.new(__dir__)
76
+ .join('../app/controllers/active_element')
77
+ .glob('**/*_controller.rb')
78
+ .each { |path| require path }
79
+ Rails.root.join('app/controllers/admin/').glob('**/*_controller.rb').each { |path| require path }
80
+ end
81
+
82
+ private
83
+
84
+ def inferred_navbar_items(user)
85
+ eager_load_controllers
86
+ user_routes(user).available.select(&:primary?).map do |route|
87
+ { path: route.path, title: route.title, spec: route.spec }
88
+ end
89
+ end
90
+
91
+ def user_routes(user)
92
+ ActiveElement::Routes.new(
93
+ permissions: user.permissions,
94
+ rails_component: ActiveElement::RailsComponent.new(Rails)
95
+ )
96
+ end
97
+ end
8
98
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :active_element do
4
+ desc 'Displays all permissions used by this application'
5
+ task permissions: :environment do
6
+ ActiveElement.eager_load_controllers
7
+ routes = ActiveElement::Routes.new(rails_component: ActiveElement::RailsComponent.new(Rails))
8
+ permissions = routes.map(&:permissions).flatten.sort.uniq
9
+ $stdout.puts ActiveElement::ColorizedString.new(
10
+ "\nThe following user permissions are used by this application:\n",
11
+ color: :light_blue
12
+ ).value
13
+ permissions.each do |permission|
14
+ color = { list: :cyan, view: :blue, create: :green, delete: :red, edit: :yellow }.find do |action, _|
15
+ permission.include?("_#{action}_")
16
+ end&.last || :purple
17
+ $stdout.puts(' ' \
18
+ "#{ActiveElement::ColorizedString.new('*', color: :white).value} " \
19
+ "#{ActiveElement::ColorizedString.new(permission, color: color).value}")
20
+ end
21
+ $stdout.puts
22
+ end
23
+ end
@@ -0,0 +1 @@
1
+ ../spec/dummy/
@@ -0,0 +1 @@
1
+ # Forms
@@ -0,0 +1,47 @@
1
+ # Tables
2
+
3
+ ## Collection Table
4
+
5
+ The _Collection Table_ component provides a vertical table containing a collection of items item. Use with _Active Record_ model instances, an array of objects that extend `ActiveModel::Naming`, or simple hash-like objects.
6
+
7
+ Field types are automatically inferred from their respective database columns and rendered using an appropriate formatter.
8
+
9
+ ```rspec:html
10
+ class User < ActiveRecord::Base
11
+ end
12
+
13
+ collection = [
14
+ User.new(name: 'John', email: 'john@example.com'),
15
+ User.new(name: 'Jane', email: 'jane@example.org')
16
+ ]
17
+
18
+ html = active_element_component.table collection: collection, fields: [:name, :email]
19
+
20
+ it_documents html do
21
+ expect(html).to include 'John'
22
+ end
23
+ ```
24
+
25
+ ## Item Table
26
+
27
+ The _Item Table_ component provides a horizontal table containing a single item and its attributes. It supports the same item types as [Collection Tables](##Collection Table).
28
+
29
+ ```rspec:html
30
+ class User < ActiveRecord::Base
31
+ end
32
+
33
+ item = User.new(name: 'John', email: 'john@example.com', overview: 'Writes Ruby code for a living.')
34
+
35
+ html = active_element_component.table item: item, fields: [:name, :email, :password, :secret]
36
+
37
+ it_documents html do
38
+ expect(html).to include 'John'
39
+ end
40
+ ```
41
+
42
+ ```rspec
43
+ foo = { foo: 'bar', baz: 'qux' }
44
+ it_documents foo do
45
+ expect('hello').to eql 'hello'
46
+ end
47
+ ```
@@ -0,0 +1 @@
1
+ # Tabs
@@ -0,0 +1 @@
1
+ # Components
@@ -0,0 +1 @@
1
+ # Inline Decorators
@@ -0,0 +1 @@
1
+ # View Decorators
@@ -0,0 +1,3 @@
1
+ # ActiveElement
2
+
3
+ _ActiveElement_ provides a number of components for use in _Ruby on Rails_ views as well as a flexible auhorization model.
@@ -0,0 +1 @@
1
+ # Internationalization
@@ -0,0 +1,35 @@
1
+ require 'active_element'
2
+ require_relative 'dummy/config/environment'
3
+
4
+ RSpec::Documentation.configure do |config|
5
+ ActiveRecord::Migration.class_eval do
6
+ drop_table :users
7
+ rescue ActiveRecord::StatementInvalid
8
+ # Skip
9
+ end
10
+
11
+ ActiveRecord::Migration.class_eval do
12
+ create_table :users do |t|
13
+ t.string :name
14
+ t.string :email
15
+ t.text :overview
16
+ end
17
+ end
18
+
19
+ config.context do |context|
20
+ class StubbedController < ActiveElement::ApplicationController
21
+
22
+ def initialize(*args, &block)
23
+ append_view_path File.expand_path(File.join(__dir__, '../app/views/'))
24
+
25
+ super
26
+ end
27
+
28
+ def params
29
+ {}
30
+ end
31
+ end
32
+
33
+ context.active_element_component = ActiveElement::Component.new(StubbedController.new)
34
+ end
35
+ end