active_element 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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 +11 -2
  5. data/Gemfile.lock +230 -3
  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.2'
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