spotlight_search 0.1.7 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +82 -71
- data/README.md +92 -45
- data/app/controllers/spotlight_search/export_jobs_controller.rb +7 -7
- data/app/jobs/spotlight_search/export_job.rb +50 -20
- data/app/mailers/spotlight_search/export_mailer.rb +6 -0
- data/app/views/spotlight_search/export_mailer/send_error_message.html.erb +7 -0
- data/lib/generators/spotlight_search/filter_generator.rb +18 -0
- data/lib/generators/spotlight_search/install_generator.rb +31 -0
- data/lib/generators/spotlight_search/templates/application.js +15 -0
- data/lib/generators/spotlight_search/templates/coffee.js +6 -0
- data/lib/generators/spotlight_search/templates/controller.rb.erb +91 -0
- data/lib/generators/spotlight_search/templates/environment.js +19 -0
- data/lib/generators/spotlight_search/templates/filters.html.erb +12 -0
- data/lib/generators/spotlight_search/templates/scaffolds.coffee +6 -0
- data/lib/generators/spotlight_search/templates/spotlight_search.rb +1 -0
- data/lib/generators/spotlight_search/templates/webpacker.yml +102 -0
- data/lib/generators/spotlight_search/templates/webpacker_gem_assets.rb +55 -0
- data/lib/spotlight_search.rb +10 -1
- data/lib/spotlight_search/exceptions.rb +1 -1
- data/lib/spotlight_search/exportable_columns.rb +27 -32
- data/lib/spotlight_search/exportable_columns_v2.rb +90 -0
- data/lib/spotlight_search/helpers.rb +58 -9
- data/lib/spotlight_search/utils.rb +69 -0
- data/lib/spotlight_search/version.rb +1 -1
- data/spotlight_search.gemspec +3 -4
- metadata +27 -27
@@ -6,5 +6,11 @@ module SpotlightSearch
|
|
6
6
|
attachments[file_path.split('/').last] = File.read(file_path)
|
7
7
|
mail(to: email, subject: subject)
|
8
8
|
end
|
9
|
+
|
10
|
+
def send_error_message(email, err)
|
11
|
+
@error_message = err.message
|
12
|
+
mail(to: email, subject: "Error generating CSV file")
|
13
|
+
Rollbar.error(err)
|
14
|
+
end
|
9
15
|
end
|
10
16
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module SpotlightSearch
|
2
|
+
module Generators
|
3
|
+
class FilterGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
5
|
+
|
6
|
+
argument :model, type: :string, required: true, desc: "Pass a model name"
|
7
|
+
class_option :filters, aliases: "-f", type: :array, desc: "Pass filters and scopes as an array. E.x: date_filter:datetime search:input"
|
8
|
+
|
9
|
+
def copy_filter_contents_to_app
|
10
|
+
if @options.filters?
|
11
|
+
template 'filters.html.erb', "app/views/admin/#{model}/_filters.html.slim"
|
12
|
+
template 'controller.rb.erb', "app/controllers/admin/#{model}_controller.rb"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module SpotlightSearch
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
|
8
|
+
def copy_webpacker_gem_assets
|
9
|
+
copy_file 'webpacker_gem_assets.rb', 'config/initializers/webpacker_gem_assets.rb'
|
10
|
+
copy_file 'webpacker.yml', 'config/webpacker.yml'
|
11
|
+
copy_file 'environment.js', 'config/webpack/environment.js'
|
12
|
+
copy_file 'coffee.js', 'config/webpack/loaders/coffee.js'
|
13
|
+
copy_file 'spotlight_search.rb', 'config/initializers/spotlight_search.rb'
|
14
|
+
route "mount SpotlightSearch::Engine => '/spotlight_search'"
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_essentials
|
18
|
+
system("yarn add jquery")
|
19
|
+
system("yarn add coffeescript")
|
20
|
+
system("yarn add select2")
|
21
|
+
inject_into_file 'app/assets/stylesheets/application.css.scss', before: " */" do
|
22
|
+
" *= require select2\n"
|
23
|
+
end
|
24
|
+
template "application.js", "app/javascript/packs/application.js"
|
25
|
+
template 'scaffolds.coffee', "app/javascript/application/coffee_scripts/scaffolds.coffee"
|
26
|
+
gem 'kaminari', '~> 1.2.1' unless File.readlines("Gemfile").grep(/kaminari/).size > 0
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// This file is automatically compiled by Webpack, along with any other files
|
2
|
+
// present in this directory. You're encouraged to place your actual application logic in
|
3
|
+
// a relevant structure within app/javascript and only use these pack files to reference
|
4
|
+
// that code so it'll be compiled.
|
5
|
+
|
6
|
+
|
7
|
+
require("@rails/ujs").start()
|
8
|
+
require("turbolinks").start()
|
9
|
+
require("@rails/activestorage").start()
|
10
|
+
require('spotlight_search')
|
11
|
+
|
12
|
+
import jQuery from 'jquery';
|
13
|
+
window.$ = jQuery
|
14
|
+
window.jQuery = jQuery
|
15
|
+
import 'select2';
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Admin::<%= model.titleize %>Controller < ApplicationController
|
2
|
+
before_action :set_<%= model.singularize %>, only: [:show, :edit, :update, :destroy]
|
3
|
+
|
4
|
+
# GET /<%= model %>
|
5
|
+
# GET /<%= model %>.json
|
6
|
+
def index
|
7
|
+
@filtered_result = <%= model.classify %>.all.filter_by(params[:page], filter_params.to_h, sort_params.to_h)
|
8
|
+
respond_to do |format|
|
9
|
+
if request.xhr?
|
10
|
+
format.html { render partial: 'table' }
|
11
|
+
format.json
|
12
|
+
else
|
13
|
+
format.html
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# GET /<%= model %>/1
|
19
|
+
# GET /<%= model %>/1.json
|
20
|
+
def show
|
21
|
+
end
|
22
|
+
|
23
|
+
# GET /<%= model %>/new
|
24
|
+
def new
|
25
|
+
@<%= model.singularize %> = Profile.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# GET /<%= model %>/1/edit
|
29
|
+
def edit
|
30
|
+
end
|
31
|
+
|
32
|
+
# POST /<%= model %>
|
33
|
+
# POST /<%= model %>.json
|
34
|
+
def create
|
35
|
+
@<%= model.singularize %> = Profile.new(<%= model.singularize %>_params)
|
36
|
+
|
37
|
+
respond_to do |format|
|
38
|
+
if @<%= model.singularize %>.save
|
39
|
+
format.html { redirect_to @<%= model.singularize %>, notice: 'Profile was successfully created.' }
|
40
|
+
format.json { render :show, status: :created, location: @<%= model.singularize %> }
|
41
|
+
else
|
42
|
+
format.html { render :new }
|
43
|
+
format.json { render json: @<%= model.singularize %>.errors, status: :unprocessable_entity }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# PATCH/PUT /<%= model %>/1
|
49
|
+
# PATCH/PUT /<%= model %>/1.json
|
50
|
+
def update
|
51
|
+
respond_to do |format|
|
52
|
+
if @<%= model.singularize %>.update(<%= model.singularize %>_params)
|
53
|
+
format.html { redirect_to @<%= model.singularize %>, notice: 'Profile was successfully updated.' }
|
54
|
+
format.json { render :show, status: :ok, location: @<%= model.singularize %> }
|
55
|
+
else
|
56
|
+
format.html { render :edit }
|
57
|
+
format.json { render json: @<%= model.singularize %>.errors, status: :unprocessable_entity }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# DELETE /<%= model %>/1
|
63
|
+
# DELETE /<%= model %>/1.json
|
64
|
+
def destroy
|
65
|
+
@<%= model.singularize %>.destroy
|
66
|
+
respond_to do |format|
|
67
|
+
format.html { redirect_to <%= model.singularize %>s_url, notice: 'Profile was successfully destroyed.' }
|
68
|
+
format.json { head :no_content }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Use callbacks to share common setup or constraints between actions.
|
75
|
+
def set_<%= model.singularize %>
|
76
|
+
@<%= model.singularize %> = Profile.find(params[:id])
|
77
|
+
end
|
78
|
+
|
79
|
+
# Only allow a list of trusted parameters through.
|
80
|
+
def <%= model.singularize %>_params
|
81
|
+
params.require(:<%= model.singularize %>).permit()
|
82
|
+
end
|
83
|
+
|
84
|
+
def filter_params
|
85
|
+
params.require(:filters).permit(<%= @options.filters.map{ |f| ':' + f.split(':')[0] }.join(', ') %>) if params[:filters]
|
86
|
+
end
|
87
|
+
|
88
|
+
def sort_params
|
89
|
+
params.require(:sort).permit(:sort_column, :sort_direction) if params[:sort]
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
const { environment } = require('@rails/webpacker')
|
2
|
+
const coffee = require('./loaders/coffee')
|
3
|
+
|
4
|
+
const webpack = require('webpack')
|
5
|
+
environment.plugins.prepend('Provide',
|
6
|
+
new webpack.ProvidePlugin({
|
7
|
+
$: require.resolve('jquery'),
|
8
|
+
jQuery: require.resolve('jquery')
|
9
|
+
})
|
10
|
+
)
|
11
|
+
|
12
|
+
const { add_paths_to_environment } = require(
|
13
|
+
// `${environment.plugins.get('Environment').defaultValues["PWD"]}/config/environments/_add_gem_paths`
|
14
|
+
`/tmp/_add_gem_paths`
|
15
|
+
)
|
16
|
+
add_paths_to_environment(environment)
|
17
|
+
|
18
|
+
environment.loaders.prepend('coffee', coffee)
|
19
|
+
module.exports = environment
|
@@ -0,0 +1,12 @@
|
|
1
|
+
= filter_wrapper({filter_url: '/admin/<%= model %>', replacement_class: "<%= model %>-table"})
|
2
|
+
<%- @options.filters.each do |filter| -%>
|
3
|
+
<%- filter_arr = filter.split(':') -%>
|
4
|
+
<%- if filter_arr[1].eql?('input') -%>
|
5
|
+
= cm_textfield_tag({behaviour: "filter", scope: "<%= filter_arr[0] %>", type: "input-filter"}, nil, "<%= filter_arr[0].titleize %>")
|
6
|
+
<%- elsif filter_arr[1].eql?('select') -%>
|
7
|
+
= cm_select_tag([["<%= model.titleize %>", "<%= model %>"]], {behaviour: 'filter', scope: "<%= filter_arr[0] %>", type: 'select-filter', placeholder: ''}, "select2-single")
|
8
|
+
<%- elsif filter_arr[1].eql?('multi_select') -%>
|
9
|
+
= cm_multi_select_tag([["<%= model.titleize %>", "<%= model %>"]], {behaviour: 'filter', scope: "<%= filter_arr[0] %>", type: 'select-filter', placeholder: ''}, "select2-multiple")
|
10
|
+
<%- end -%>
|
11
|
+
<%- end -%>
|
12
|
+
= clear_filters(admin_<%= model %>_path, nil, nil, "Clear all")
|
@@ -0,0 +1 @@
|
|
1
|
+
ActiveRecord::Base.include SpotlightSearch::ExportableColumns
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Note: You must restart bin/webpack-dev-server for changes to take effect
|
2
|
+
|
3
|
+
default: &default
|
4
|
+
source_path: app/javascript
|
5
|
+
source_entry_path: packs
|
6
|
+
public_root_path: public
|
7
|
+
public_output_path: packs
|
8
|
+
cache_path: tmp/cache/webpacker
|
9
|
+
check_yarn_integrity: false
|
10
|
+
webpack_compile_output: true
|
11
|
+
|
12
|
+
# Additional paths webpack should lookup modules
|
13
|
+
# ['app/assets', 'engine/foo/app/assets']
|
14
|
+
resolved_paths: []
|
15
|
+
resolved_gems_output_path: '/tmp/_add_gem_paths.js'
|
16
|
+
resolved_gems: [
|
17
|
+
'spotlight_search'
|
18
|
+
]
|
19
|
+
|
20
|
+
# Reload manifest.json on all requests so we reload latest compiled packs
|
21
|
+
cache_manifest: false
|
22
|
+
|
23
|
+
# Extract and emit a css file
|
24
|
+
extract_css: false
|
25
|
+
|
26
|
+
static_assets_extensions:
|
27
|
+
- .jpg
|
28
|
+
- .jpeg
|
29
|
+
- .png
|
30
|
+
- .gif
|
31
|
+
- .tiff
|
32
|
+
- .ico
|
33
|
+
- .svg
|
34
|
+
- .eot
|
35
|
+
- .otf
|
36
|
+
- .ttf
|
37
|
+
- .woff
|
38
|
+
- .woff2
|
39
|
+
|
40
|
+
extensions:
|
41
|
+
- .coffee
|
42
|
+
- .mjs
|
43
|
+
- .js
|
44
|
+
- .sass
|
45
|
+
- .scss
|
46
|
+
- .css
|
47
|
+
- .module.sass
|
48
|
+
- .module.scss
|
49
|
+
- .module.css
|
50
|
+
- .png
|
51
|
+
- .svg
|
52
|
+
- .gif
|
53
|
+
- .jpeg
|
54
|
+
- .jpg
|
55
|
+
|
56
|
+
development:
|
57
|
+
<<: *default
|
58
|
+
compile: true
|
59
|
+
|
60
|
+
# Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
|
61
|
+
check_yarn_integrity: true
|
62
|
+
|
63
|
+
# Reference: https://webpack.js.org/configuration/dev-server/
|
64
|
+
dev_server:
|
65
|
+
https: false
|
66
|
+
host: localhost
|
67
|
+
port: 3035
|
68
|
+
public: localhost:3035
|
69
|
+
hmr: false
|
70
|
+
# Inline should be set to true if using HMR
|
71
|
+
inline: true
|
72
|
+
overlay: true
|
73
|
+
compress: true
|
74
|
+
disable_host_check: true
|
75
|
+
use_local_ip: false
|
76
|
+
quiet: false
|
77
|
+
pretty: false
|
78
|
+
headers:
|
79
|
+
'Access-Control-Allow-Origin': '*'
|
80
|
+
watch_options:
|
81
|
+
ignored: '**/node_modules/**'
|
82
|
+
|
83
|
+
|
84
|
+
test:
|
85
|
+
<<: *default
|
86
|
+
compile: true
|
87
|
+
|
88
|
+
# Compile test packs to a separate directory
|
89
|
+
public_output_path: packs-test
|
90
|
+
|
91
|
+
production:
|
92
|
+
<<: *default
|
93
|
+
webpack_compile_output: true
|
94
|
+
|
95
|
+
# Production depends on precompilation of packs prior to booting for performance.
|
96
|
+
compile: false
|
97
|
+
|
98
|
+
# Extract and emit a css file
|
99
|
+
extract_css: true
|
100
|
+
|
101
|
+
# Cache manifest.json for performance
|
102
|
+
cache_manifest: true
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# config/initializers/webpacker_gem_assets.rb
|
2
|
+
|
3
|
+
def default_assets_path
|
4
|
+
'app/assets/javascripts'
|
5
|
+
end
|
6
|
+
|
7
|
+
def output_path
|
8
|
+
Webpacker.config.send(:data)[:resolved_gems_output_path] || '/tmp/_add_gem_paths.js'
|
9
|
+
end
|
10
|
+
|
11
|
+
def resolve_gem_path(gem)
|
12
|
+
if gem.present?
|
13
|
+
gem_path = Gem.loaded_specs[gem]&.full_gem_path
|
14
|
+
if gem_path.present?
|
15
|
+
return "#{gem_path}/#{default_assets_path}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
abort("Gem '#{gem}' not found, please check webpacker config (#{Webpacker.config.config_path})")
|
19
|
+
end
|
20
|
+
|
21
|
+
buffer = []
|
22
|
+
buffer << "
|
23
|
+
/*
|
24
|
+
* THIS IS A GENERATED FILE.
|
25
|
+
* DO NOT CHECK IT INTO SOURCE CONTROL
|
26
|
+
*/
|
27
|
+
function add_paths_to_environment(environment) {".strip
|
28
|
+
|
29
|
+
resolved_gems = Webpacker.config.send(:data)[:resolved_gems]
|
30
|
+
if resolved_gems.any?
|
31
|
+
buffer << "\n environment.resolvedModules.add(\n"
|
32
|
+
buffer << resolved_gems.map do |gem|
|
33
|
+
" { key: 'gem-#{gem}', value: '#{resolve_gem_path(gem)}' }"
|
34
|
+
end.join(",\n")
|
35
|
+
buffer << "\n )\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
buffer << "}\nexports.add_paths_to_environment = add_paths_to_environment\n"
|
39
|
+
|
40
|
+
File.write(
|
41
|
+
output_path,
|
42
|
+
buffer.join
|
43
|
+
)
|
44
|
+
|
45
|
+
module WebpackerGemAssets
|
46
|
+
def resolved_paths
|
47
|
+
self.send(:data)[:resolved_gems].map do |gem|
|
48
|
+
resolve_gem_path gem
|
49
|
+
end.compact.concat(super)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Webpacker::Configuration
|
54
|
+
prepend WebpackerGemAssets
|
55
|
+
end
|
data/lib/spotlight_search.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'spotlight_search/engine'
|
2
2
|
require 'spotlight_search/version'
|
3
3
|
require 'spotlight_search/exportable_columns'
|
4
|
+
require 'spotlight_search/exportable_columns_v2'
|
5
|
+
require 'spotlight_search/utils'
|
4
6
|
require 'spotlight_search/railtie' if defined?(Rails)
|
5
7
|
require 'active_support'
|
6
8
|
require 'active_support/rails'
|
@@ -10,10 +12,17 @@ module SpotlightSearch
|
|
10
12
|
|
11
13
|
autoload :Exceptions, 'spotlight_search/exceptions'
|
12
14
|
|
15
|
+
def self.setup
|
16
|
+
yield self
|
17
|
+
end
|
18
|
+
|
19
|
+
mattr_accessor :exportable_columns_version
|
20
|
+
@@exportable_columns_version = :v1
|
21
|
+
|
13
22
|
module ClassMethods
|
14
23
|
def filter_by(page, filter_params = {}, sort_params = {})
|
15
24
|
filtered_result = OpenStruct.new
|
16
|
-
sort_column = self.column_names.include?(sort_params[:sort_column]) ? sort_params[:sort_column] : "created_at"
|
25
|
+
sort_column = self.column_names.include?(sort_params[:sort_column]) ? sort_params[:sort_column] : "#{self.table_name}.created_at"
|
17
26
|
sort_direction = %w[asc desc].include?(sort_params[:sort_direction]) ? sort_params[:sort_direction] : "asc"
|
18
27
|
sort_params = {sort_column: sort_column, sort_direction: sort_direction}
|
19
28
|
raw_data = self.filter(filter_params).sort_list(sort_column, sort_direction)
|
@@ -5,7 +5,7 @@ module SpotlightSearch
|
|
5
5
|
module ClassMethods
|
6
6
|
# Enables or disables export and specifies which all columns can be
|
7
7
|
# exported. For enabling export for all columns in all models
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# class ApplicationRecord < ActiveRecord::Base
|
10
10
|
# export_columns enabled: true
|
11
11
|
# end
|
@@ -16,7 +16,7 @@ module SpotlightSearch
|
|
16
16
|
# export_columns enabled: false
|
17
17
|
# end
|
18
18
|
#
|
19
|
-
# For allowing export for only specific columns in a model
|
19
|
+
# For allowing export for only specific columns in a model
|
20
20
|
#
|
21
21
|
# class Person < ActiveRecord::Base
|
22
22
|
# export_columns enabled: true, only: [:created_at, :updated_at]
|
@@ -28,41 +28,36 @@ module SpotlightSearch
|
|
28
28
|
# export_columns enabled: true, except: [:created_at, :updated_at]
|
29
29
|
# end
|
30
30
|
#
|
31
|
-
def export_columns(enabled: false, only: nil, except: nil)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
self.enabled_columns = self.enabled_columns - except
|
52
|
-
end
|
53
|
-
else
|
54
|
-
self.export_enabled = false
|
55
|
-
self.enabled_columns = nil
|
56
|
-
end
|
31
|
+
def export_columns(enabled: false, only: nil, except: nil, associated: nil)
|
32
|
+
ActiveRecord::Base.connection.migration_context.needs_migration? && return
|
33
|
+
return unless enabled
|
34
|
+
|
35
|
+
self.export_enabled = true
|
36
|
+
all_columns = self.column_names.map(&:to_sym)
|
37
|
+
if only.present?
|
38
|
+
unless (valid_columns = only & all_columns).size == only.size
|
39
|
+
invalid_columns = only - valid_columns
|
40
|
+
raise SpotlightSearch::Exceptions::InvalidColumns, invalid_columns
|
41
|
+
end
|
42
|
+
self.enabled_columns = only
|
43
|
+
else
|
44
|
+
self.enabled_columns = all_columns
|
45
|
+
end
|
46
|
+
if except.present?
|
47
|
+
unless (valid_columns = except & all_columns).size == except.size
|
48
|
+
invalid_columns = except - valid_columns
|
49
|
+
raise SpotlightSearch::Exceptions::InvalidColumns, invalid_columns
|
57
50
|
end
|
58
|
-
|
51
|
+
self.enabled_columns = self.enabled_columns - except
|
59
52
|
end
|
53
|
+
rescue ActiveRecord::NoDatabaseError
|
54
|
+
Rails.logger.info("No database error")
|
60
55
|
end
|
61
56
|
|
62
57
|
# Validates whether the selected columns are allowed for export
|
63
58
|
def validate_exportable_columns(columns)
|
64
59
|
unless columns.is_a?(Array)
|
65
|
-
raise SpotlightSearch::Exceptions::InvalidValue.new('
|
60
|
+
raise SpotlightSearch::Exceptions::InvalidValue.new('Expected Array. Invalid type received')
|
66
61
|
end
|
67
62
|
unless (self.enabled_columns & columns.map(&:to_sym)) == columns.size
|
68
63
|
return false
|
@@ -72,8 +67,8 @@ module SpotlightSearch
|
|
72
67
|
end
|
73
68
|
|
74
69
|
included do
|
75
|
-
class_attribute :enabled_columns, instance_accessor: false
|
76
|
-
class_attribute :export_enabled, instance_accessor: false
|
70
|
+
class_attribute :enabled_columns, instance_accessor: false, default: nil
|
71
|
+
class_attribute :export_enabled, instance_accessor: false, default: false
|
77
72
|
end
|
78
73
|
end
|
79
74
|
end
|