para 0.5.4 → 0.6.2
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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/para/admin/async-progress.coffee +29 -0
- data/app/assets/javascripts/para/admin/filters-form.coffee +1 -1
- data/app/assets/javascripts/para/admin/importers.coffee +38 -0
- data/app/assets/javascripts/para/admin/table.coffee +1 -1
- data/app/assets/javascripts/para/admin/tabs.coffee +1 -1
- data/app/assets/javascripts/para/admin/theme_actions.coffee +1 -1
- data/app/assets/javascripts/para/admin/tree.coffee +5 -5
- data/app/assets/javascripts/para/admin.coffee +2 -1
- data/app/assets/javascripts/para/inputs/nested_many.coffee +1 -1
- data/app/assets/javascripts/para/lib/remote-file-forms.coffee +60 -0
- data/app/assets/javascripts/para/lib/turbolinks-loading.coffee +1 -1
- data/app/assets/javascripts/para/lib/turbolinks-reloader.coffee +19 -0
- data/app/assets/stylesheets/para/admin/src/datetimepicker.sass +3 -1
- data/app/assets/stylesheets/para/admin/src/fuelux.sass +4 -2
- data/app/assets/stylesheets/para/admin/src/nested_many.sass +5 -0
- data/app/assets/stylesheets/para/admin/src/redactor.sass +8 -5
- data/app/assets/stylesheets/para/admin/src/selectize.sass +7 -4
- data/app/assets/stylesheets/para/admin/theme/_breadcrumb.sass +20 -12
- data/app/assets/stylesheets/para/admin/theme/_buttons.sass +6 -2
- data/app/assets/stylesheets/para/admin/theme/_checkable.sass +4 -2
- data/app/assets/stylesheets/para/admin/theme/_commonds.sass +6 -3
- data/app/assets/stylesheets/para/admin/theme/_dropdown.sass +3 -1
- data/app/assets/stylesheets/para/admin/theme/_form.sass +4 -2
- data/app/assets/stylesheets/para/admin/theme/_list.sass +7 -5
- data/app/assets/stylesheets/para/admin/theme/_navigation.sass +12 -66
- data/app/assets/stylesheets/para/admin/theme/_navtabs.sass +18 -14
- data/app/assets/stylesheets/para/admin/theme/_orderable.sass +7 -3
- data/app/assets/stylesheets/para/admin/theme/_panel.sass +19 -13
- data/app/assets/stylesheets/para/admin/theme/_tree.sass +4 -2
- data/app/assets/stylesheets/para/overrides/theme.sass +14 -10
- data/app/controllers/para/admin/base_controller.rb +0 -5
- data/app/controllers/para/admin/crud_resources_controller.rb +5 -19
- data/app/controllers/para/admin/{singleton_resources_controller.rb → form_resources_controller.rb} +4 -4
- data/app/controllers/para/admin/imports_controller.rb +68 -0
- data/app/controllers/para/admin/resources_controller.rb +0 -1
- data/app/decorators/para/component/base_decorator.rb +6 -18
- data/app/decorators/para/component/crud_decorator.rb +3 -3
- data/app/decorators/para/component/{singleton_resource_decorator.rb → form_decorator.rb} +6 -6
- data/app/helpers/para/admin/components_helper.rb +12 -1
- data/app/helpers/para/admin/decorators_helper.rb +13 -0
- data/app/helpers/para/markup_helper.rb +4 -0
- data/app/helpers/para/search_helper.rb +1 -4
- data/app/models/para/component/base.rb +17 -14
- data/app/models/para/component/crud.rb +17 -0
- data/app/models/para/component/form.rb +36 -0
- data/app/models/para/component/resource.rb +9 -20
- data/app/models/para/component/settings.rb +1 -1
- data/app/models/para/library/file.rb +23 -0
- data/app/models/para/library.rb +5 -0
- data/app/views/para/admin/form_resources/show.html.haml +4 -0
- data/app/views/para/admin/imports/_completed.html.haml +16 -0
- data/app/views/para/admin/imports/_failed.html.haml +7 -0
- data/app/views/para/admin/imports/_progress.html.haml +5 -0
- data/app/views/para/admin/imports/new.html.haml +14 -0
- data/app/views/para/admin/imports/show.html.haml +10 -0
- data/app/views/para/admin/resources/_fields.html.haml +1 -4
- data/app/views/para/admin/resources/_imports_menu.html.haml +17 -20
- data/app/views/para/admin/resources/_tree.html.haml +1 -1
- data/app/views/para/admin/shared/_breadcrumbs.html.haml +1 -4
- data/app/views/para/admin/shared/_navigation.html.haml +2 -2
- data/app/views/para/form/_tabs.html.haml +1 -1
- data/config/locales/en.yml +10 -0
- data/config/locales/fr.yml +12 -2
- data/db/migrate/20160905134106_create_para_library_files.rb +9 -0
- data/lib/generators/para/importer/templates/base_importer.rb +8 -1
- data/lib/generators/para/install/install_generator.rb +1 -2
- data/lib/generators/para/install/templates/components.rb +9 -5
- data/lib/generators/para/tree_item/tree_item_generator.rb +17 -0
- data/lib/para/attribute_field/base.rb +12 -4
- data/lib/para/attribute_field/enum.rb +7 -3
- data/lib/para/component/importable.rb +4 -4
- data/lib/para/component.rb +1 -1
- data/lib/para/components_cleaner.rb +66 -0
- data/lib/para/components_configuration.rb +3 -1
- data/lib/para/engine.rb +36 -3
- data/lib/para/ext/active_job_status.rb +13 -0
- data/lib/para/ext/paperclip.rb +10 -0
- data/lib/para/ext/simple_form_extension.rb +6 -0
- data/lib/para/ext.rb +1 -0
- data/lib/para/form_builder/nested_form.rb +5 -2
- data/lib/para/importer/base.rb +84 -7
- data/lib/para/inputs/nested_many_input.rb +1 -1
- data/lib/para/logging/active_job_log_subscriber.rb +48 -0
- data/lib/para/logging.rb +8 -0
- data/lib/para/markup/resources_table.rb +2 -4
- data/lib/para/model_field_parsers/enums.rb +19 -0
- data/lib/para/model_field_parsers/relations.rb +1 -1
- data/lib/para/model_field_parsers/store.rb +1 -1
- data/lib/para/model_field_parsers.rb +1 -0
- data/lib/para/plugins/routes.rb +2 -4
- data/lib/para/postgres_extensions_checker.rb +30 -0
- data/lib/para/routes.rb +5 -4
- data/lib/para/routing/component_controller_constraint.rb +36 -0
- data/lib/para/routing.rb +7 -0
- data/lib/para/sti/root_model.rb +8 -2
- data/lib/para/version.rb +1 -1
- data/lib/para.rb +10 -0
- data/lib/rails/routing_mapper.rb +59 -27
- data/lib/tasks/para_tasks.rake +21 -11
- metadata +73 -15
- data/app/models/para/component/singleton_resource.rb +0 -35
- data/app/views/para/admin/singleton_resources/show.html.haml +0 -4
|
@@ -6,16 +6,20 @@ module Para
|
|
|
6
6
|
def value_for(instance)
|
|
7
7
|
if (raw_value = instance.send(name)) &&
|
|
8
8
|
path = enum_path_for(instance, raw_value)
|
|
9
|
-
translation = ::I18n.t("activerecord.#{ path }", default:
|
|
9
|
+
translation = ::I18n.t("activerecord.#{ path }", default: '')
|
|
10
10
|
|
|
11
|
-
translation || raw_value
|
|
11
|
+
translation.presence || raw_value
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
def field_type
|
|
16
|
+
:selectize
|
|
17
|
+
end
|
|
18
|
+
|
|
15
19
|
private
|
|
16
20
|
|
|
17
21
|
def enum_path_for(instance, key)
|
|
18
|
-
['enums', instance.
|
|
22
|
+
['enums', instance.model_name.i18n_key, name, key].join('.')
|
|
19
23
|
end
|
|
20
24
|
end
|
|
21
25
|
end
|
|
@@ -8,14 +8,14 @@ module Para
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def importable?
|
|
11
|
-
@importable ||=
|
|
11
|
+
@importable ||= importers.length > 0
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
# TODO : Move :configuration column store to JSON instead of HStore
|
|
15
15
|
# which handles more data types and will help us avoid eval here
|
|
16
|
-
def
|
|
17
|
-
@
|
|
18
|
-
eval(importers)
|
|
16
|
+
def importers
|
|
17
|
+
@importers ||= if (importers = configuration['importers'].presence)
|
|
18
|
+
eval(importers).map(&:constantize)
|
|
19
19
|
else
|
|
20
20
|
[]
|
|
21
21
|
end
|
data/lib/para/component.rb
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
class ComponentsCleaner
|
|
3
|
+
# Hide class instanciation
|
|
4
|
+
def self.run; new.run; end
|
|
5
|
+
|
|
6
|
+
def run
|
|
7
|
+
components.each do |component|
|
|
8
|
+
unless component == Para.components.component_for(component.identifier)
|
|
9
|
+
component.destroy
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Para::ComponentSection.find_each do |section|
|
|
14
|
+
unless Para.components.section_for(section.identifier)
|
|
15
|
+
section.destroy
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def components
|
|
23
|
+
Para::Component::Base.all.to_a
|
|
24
|
+
rescue ActiveRecord::SubclassNotFound => e
|
|
25
|
+
# When a subclass is not found, define it and try loading all components.
|
|
26
|
+
#
|
|
27
|
+
# Since we're probably about to destroy this class, this sould not
|
|
28
|
+
# produce errors for the end user
|
|
29
|
+
#
|
|
30
|
+
if (match = e.message.match(/the subclass: '([\w:]+)'/))
|
|
31
|
+
define_temporary_component_subclass(match[1])
|
|
32
|
+
return components
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
raise e
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def define_temporary_component_subclass(subclass_name)
|
|
39
|
+
subclass_path = subclass_name.split('::')
|
|
40
|
+
|
|
41
|
+
subclass_path.each_with_index.each_with_object([]) do |(const_name, index), path|
|
|
42
|
+
parent_name = path.join('::').presence
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
[parent_name, const_name].compact.join('::').constantize
|
|
46
|
+
rescue NameError
|
|
47
|
+
# Last iterated constnat is the actual component subclass, others must
|
|
48
|
+
# be modules namespacing the component class
|
|
49
|
+
object = if index == (subclass_path.length - 1)
|
|
50
|
+
Class.new(Para::Component::Base)
|
|
51
|
+
else
|
|
52
|
+
Module.new
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
parent = parent_name ? parent_name.constantize : Object
|
|
56
|
+
# Define module or class in parent namespace
|
|
57
|
+
parent.const_set(const_name, object)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Add const name to the path to allow namespaced constants to be
|
|
61
|
+
# defined in next loop iteration
|
|
62
|
+
path << const_name
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -86,6 +86,8 @@ module Para
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
tables_exist
|
|
89
|
+
rescue ActiveRecord::NoDatabaseError
|
|
90
|
+
false # Do not load components when the database is not installed
|
|
89
91
|
end
|
|
90
92
|
|
|
91
93
|
# Eager loads every file ending with _component.rb that's included in a
|
|
@@ -151,7 +153,7 @@ module Para
|
|
|
151
153
|
|
|
152
154
|
def refresh(attributes = {})
|
|
153
155
|
self.model = type.where(identifier: identifier).first_or_initialize
|
|
154
|
-
model.
|
|
156
|
+
model.update_with(attributes.merge(options_with_defaults))
|
|
155
157
|
model.save!
|
|
156
158
|
end
|
|
157
159
|
|
data/lib/para/engine.rb
CHANGED
|
@@ -29,7 +29,7 @@ module Para
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
initializer 'Extend paperclip attachment definition' do
|
|
32
|
-
|
|
32
|
+
next unless Kernel.const_defined?('Paperclip')
|
|
33
33
|
|
|
34
34
|
ActiveSupport.on_load(:active_record) do
|
|
35
35
|
::Paperclip::HasAttachedFile.send(
|
|
@@ -39,7 +39,7 @@ module Para
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
initializer 'Extend cancan ControllerResource class' do
|
|
42
|
-
|
|
42
|
+
next unless Kernel.const_defined?('CanCan')
|
|
43
43
|
|
|
44
44
|
ActiveSupport.on_load(:active_record) do
|
|
45
45
|
::CanCan::ControllerResource.send(
|
|
@@ -56,6 +56,14 @@ module Para
|
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
initializer 'Extend active job status\' status class' do
|
|
60
|
+
ActiveSupport.on_load(:active_job) do
|
|
61
|
+
::ActiveJob::Status::Status.send(
|
|
62
|
+
:include, Para::Ext::ActiveJob::StatusMixin
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
59
67
|
initializer 'Build components tree' do |app|
|
|
60
68
|
components_config_path = Rails.root.join('config', 'components.rb')
|
|
61
69
|
|
|
@@ -65,7 +73,32 @@ module Para
|
|
|
65
73
|
end
|
|
66
74
|
|
|
67
75
|
initializer 'Load page sections' do |app|
|
|
68
|
-
|
|
76
|
+
app.config.to_prepare do
|
|
77
|
+
Para::Page::Section.eager_load!
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
initializer 'Check for extensions installation' do
|
|
82
|
+
Para::PostgresExtensionsChecker.check_all
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
initializer 'Configure ActiveJob' do
|
|
86
|
+
if ActiveSupport::Cache::NullStore === ActiveJob::Status.store
|
|
87
|
+
ActiveJob::Status.store = :memory_store
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
Para::Logging::ActiveJobLogSubscriber.attach_to :active_job
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Allow generating resources in the gem without having all the unnecessary
|
|
94
|
+
# files generated
|
|
95
|
+
#
|
|
96
|
+
config.generators do |generators|
|
|
97
|
+
generators.skip_routes true if generators.respond_to?(:skip_routes)
|
|
98
|
+
generators.helper false
|
|
99
|
+
generators.stylesheets false
|
|
100
|
+
generators.javascripts false
|
|
101
|
+
generators.test_framework false
|
|
69
102
|
end
|
|
70
103
|
end
|
|
71
104
|
end
|
data/lib/para/ext/paperclip.rb
CHANGED
data/lib/para/ext.rb
CHANGED
|
@@ -56,7 +56,7 @@ module Para
|
|
|
56
56
|
return false unless nested?
|
|
57
57
|
|
|
58
58
|
reflection = parent_object.class.reflect_on_association(nested_attribute_name)
|
|
59
|
-
reflection && (reflection.options[:inverse_of] == field_name)
|
|
59
|
+
reflection && (reflection.options[:inverse_of].to_s == field_name.to_s)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def nested?
|
|
@@ -64,7 +64,10 @@ module Para
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def nested_attribute_name
|
|
67
|
-
options[:nested_attribute_name]
|
|
67
|
+
options[:nested_attribute_name] ||=
|
|
68
|
+
if (match = object_name.match(/\[(\w+)_attributes\]/))
|
|
69
|
+
match[1]
|
|
70
|
+
end
|
|
68
71
|
end
|
|
69
72
|
|
|
70
73
|
def parent_object
|
data/lib/para/importer/base.rb
CHANGED
|
@@ -1,23 +1,100 @@
|
|
|
1
1
|
module Para
|
|
2
2
|
module Importer
|
|
3
|
-
class Base
|
|
4
|
-
|
|
3
|
+
class Base < ActiveJob::Base
|
|
4
|
+
include ActiveJob::Status
|
|
5
|
+
# Used to store import errors on the object
|
|
6
|
+
include ActiveModel::Validations
|
|
7
|
+
# Used to translate importer name with rails default `activemodel` i18n keys
|
|
8
|
+
extend ActiveModel::Naming
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
rescue_from Exception, with: :rescue_exception
|
|
11
|
+
|
|
12
|
+
class_attribute :allows_import_errors
|
|
13
|
+
|
|
14
|
+
attr_reader :file, :sheet
|
|
15
|
+
|
|
16
|
+
def perform(file, options = {})
|
|
17
|
+
@file = file
|
|
18
|
+
@sheet = Roo::Spreadsheet.open(file.attachment_path, options)
|
|
19
|
+
progress.total = sheet.last_row - 1
|
|
9
20
|
|
|
10
|
-
def run
|
|
11
21
|
ActiveRecord::Base.transaction do
|
|
12
22
|
(2..(sheet.last_row)).each do |index|
|
|
13
|
-
|
|
23
|
+
begin
|
|
24
|
+
progress.increment
|
|
25
|
+
import_from_row(sheet.row(index))
|
|
26
|
+
rescue ActiveRecord::RecordInvalid => error
|
|
27
|
+
if allows_import_errors?
|
|
28
|
+
add_errors_from(index, error.record)
|
|
29
|
+
else
|
|
30
|
+
raise
|
|
31
|
+
end
|
|
32
|
+
end
|
|
14
33
|
end
|
|
15
34
|
end
|
|
35
|
+
|
|
36
|
+
status.update(errors: errors.full_messages)
|
|
16
37
|
end
|
|
17
38
|
|
|
39
|
+
private
|
|
40
|
+
|
|
18
41
|
def import_from_row(row)
|
|
19
42
|
raise '#import_from_row(row) must be defined'
|
|
20
43
|
end
|
|
44
|
+
|
|
45
|
+
def add_errors_from(index, record)
|
|
46
|
+
# The file's row number starts at 1 and headers are striped, so we
|
|
47
|
+
# add 2 to the index to obtain the row number
|
|
48
|
+
row_name = I18n.t('para.import.row_error_prefix', number: index)
|
|
49
|
+
|
|
50
|
+
record.errors.messages.each do |attribute, messages|
|
|
51
|
+
messages.each do |message|
|
|
52
|
+
full_message = record.errors.full_message(attribute, message)
|
|
53
|
+
|
|
54
|
+
if (value = record.send(attribute)).presence
|
|
55
|
+
full_message << ' (<b>' << value << '</b>)'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
errors.add(row_name, full_message)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def allows_import_errors?
|
|
64
|
+
!!self.class.allows_import_errors
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.allow_import_errors!
|
|
68
|
+
self.allows_import_errors = true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def headers
|
|
72
|
+
@headers ||= sheet.row(1)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def rescue_exception(exception)
|
|
76
|
+
status.update(status: :failed)
|
|
77
|
+
|
|
78
|
+
tag_logger(self.class.name, self.job_id) do
|
|
79
|
+
ActiveSupport::Notifications.instrument "failed.active_job",
|
|
80
|
+
adapter: self.class.queue_adapter, job: self, exception: exception
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
if defined?(ExceptionNotifier)
|
|
84
|
+
ExceptionNotifier.notify_exception(
|
|
85
|
+
exception, data: {
|
|
86
|
+
importer: self.class.name,
|
|
87
|
+
payload: {
|
|
88
|
+
file_type: file.class,
|
|
89
|
+
file_id: file.id,
|
|
90
|
+
file_name: file.attachment_file_name
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
raise exception
|
|
97
|
+
end
|
|
21
98
|
end
|
|
22
99
|
end
|
|
23
100
|
end
|
|
@@ -11,7 +11,7 @@ module Para
|
|
|
11
11
|
# Load existing resources
|
|
12
12
|
resources = object.send(attribute_name)
|
|
13
13
|
# Order them if the list should be orderable
|
|
14
|
-
resources = resources.
|
|
14
|
+
resources = resources.sort_by(&:position) if orderable
|
|
15
15
|
|
|
16
16
|
locals = options.fetch(:locals, {})
|
|
17
17
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module Logging
|
|
3
|
+
class ActiveJobLogSubscriber < ActiveSupport::LogSubscriber
|
|
4
|
+
def failed(event)
|
|
5
|
+
fatal do
|
|
6
|
+
job = event.payload[:job]
|
|
7
|
+
buffer = []
|
|
8
|
+
buffer << "#{ job.class.name } (Job ID: #{ job.job_id }) failed" + args_info(job)
|
|
9
|
+
buffer << ''
|
|
10
|
+
buffer << [event.payload[:exception].class, event.payload[:exception].message].join(' - ')
|
|
11
|
+
buffer << ''
|
|
12
|
+
buffer << '-------------------'
|
|
13
|
+
buffer << ''
|
|
14
|
+
buffer += event.payload[:exception].backtrace
|
|
15
|
+
buffer << ''
|
|
16
|
+
buffer.compact.join("\n")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def args_info(job)
|
|
23
|
+
if job.arguments.any?
|
|
24
|
+
' with arguments: ' +
|
|
25
|
+
job.arguments.map { |arg| format(arg).inspect }.join(', ')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def format(arg)
|
|
30
|
+
case arg
|
|
31
|
+
when Hash
|
|
32
|
+
arg.transform_values { |value| format(value) }
|
|
33
|
+
when Array
|
|
34
|
+
arg.map { |value| format(value) }
|
|
35
|
+
when GlobalID::Identification
|
|
36
|
+
arg.to_global_id rescue arg
|
|
37
|
+
else
|
|
38
|
+
arg
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def logger
|
|
43
|
+
ActiveJob::Base.logger
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
data/lib/para/logging.rb
ADDED
|
@@ -79,8 +79,6 @@ module Para
|
|
|
79
79
|
field_name = nil
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
sort = options.delete(:sort) || field_name
|
|
83
|
-
|
|
84
82
|
label = if Symbol === field_name
|
|
85
83
|
model.human_attribute_name(field_name)
|
|
86
84
|
elsif block
|
|
@@ -90,7 +88,7 @@ module Para
|
|
|
90
88
|
end
|
|
91
89
|
|
|
92
90
|
content_tag(:th, options) do
|
|
93
|
-
if sort
|
|
91
|
+
if (sort = options.delete(:sort))
|
|
94
92
|
view.sort_link(search, *sort, label, hide_indicator: true)
|
|
95
93
|
elsif searchable?(field_name)
|
|
96
94
|
view.sort_link(search, field_name, label, hide_indicator: true)
|
|
@@ -155,7 +153,7 @@ module Para
|
|
|
155
153
|
}
|
|
156
154
|
|
|
157
155
|
view.link_to(path, options) do
|
|
158
|
-
content_tag(:i, '', class: 'fa fa-
|
|
156
|
+
content_tag(:i, '', class: 'fa fa-copy')
|
|
159
157
|
end
|
|
160
158
|
end
|
|
161
159
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module ModelFieldParsers
|
|
3
|
+
class Enums < Para::ModelFieldParsers::Base
|
|
4
|
+
register :enums, self
|
|
5
|
+
|
|
6
|
+
def parse!
|
|
7
|
+
model.defined_enums.each do |key, _|
|
|
8
|
+
fields_hash[key] = AttributeField::EnumField.new(
|
|
9
|
+
model, name: key, type: 'enum'
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def applicable?
|
|
15
|
+
model.respond_to?(:defined_enums) && !model.defined_enums.empty?
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -38,7 +38,7 @@ module Para
|
|
|
38
38
|
fields_hash[name] = AttributeField::HasManyField.new(
|
|
39
39
|
model, name: name, type: 'has_many', field_type: 'selectize'
|
|
40
40
|
)
|
|
41
|
-
|
|
41
|
+
elsif !reflection.options[:through]
|
|
42
42
|
fields_hash[name] = AttributeField::BelongsToField.new(
|
|
43
43
|
model, name: name, type: 'belongs_to', field_type: 'selectize'
|
|
44
44
|
)
|
data/lib/para/plugins/routes.rb
CHANGED
|
@@ -13,10 +13,8 @@ module Para
|
|
|
13
13
|
router = self.router
|
|
14
14
|
|
|
15
15
|
router.instance_eval do
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
router.instance_eval(&block)
|
|
19
|
-
end
|
|
16
|
+
scope module: [:para, identifier].join('/').to_sym do
|
|
17
|
+
router.instance_eval(&block)
|
|
20
18
|
end
|
|
21
19
|
end
|
|
22
20
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
class PostgresExtensionsChecker
|
|
3
|
+
class << self
|
|
4
|
+
def check_all
|
|
5
|
+
%w(hstore unaccent).each do |extname|
|
|
6
|
+
unless extension_exists?(extname)
|
|
7
|
+
# Could not use Rails.logger here, using puts as a temporary
|
|
8
|
+
# solution.
|
|
9
|
+
puts "[Warning] PostgreSQL \"#{ extname }\" extension is not " +
|
|
10
|
+
"installed in your database. This means that you " +
|
|
11
|
+
"missing some migrations that you can install " +
|
|
12
|
+
"with the following command : " +
|
|
13
|
+
"`rake para_engine:install:migrations` and then migrate."
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def extension_exists?(extname)
|
|
21
|
+
ActiveRecord::Base.connection.execute(
|
|
22
|
+
"SELECT COUNT(*) FROM pg_catalog.pg_extension " +
|
|
23
|
+
"WHERE extname = '#{ extname }'"
|
|
24
|
+
).first['count'].to_i > 0
|
|
25
|
+
rescue ActiveRecord::NoDatabaseError
|
|
26
|
+
true # Do not issue warning when no database is installed
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/para/routes.rb
CHANGED
|
@@ -14,11 +14,12 @@ module Para
|
|
|
14
14
|
scope module: :para do
|
|
15
15
|
namespace :admin do
|
|
16
16
|
get '/' => 'main#index'
|
|
17
|
-
|
|
18
|
-
crud_component :crud, scope: ':model'
|
|
19
|
-
singleton_resource_component :singleton, scope: ':model'
|
|
20
|
-
component :settings
|
|
21
17
|
end
|
|
18
|
+
|
|
19
|
+
# Components are namespaced into :admin in their respective methods
|
|
20
|
+
crud_component
|
|
21
|
+
form_component
|
|
22
|
+
component :settings
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
block.call if block
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Allows constraining routing to components that explicitly declares to use a
|
|
2
|
+
# given controller to manage their resources.
|
|
3
|
+
#
|
|
4
|
+
# It's mainly used to allow users to override the default controller for the
|
|
5
|
+
# resources of a given Crud or Form components without having to
|
|
6
|
+
# subclass the component and declare all the routes again
|
|
7
|
+
#
|
|
8
|
+
module Para
|
|
9
|
+
module Routing
|
|
10
|
+
class ComponentControllerConstraint
|
|
11
|
+
attr_reader :controller
|
|
12
|
+
|
|
13
|
+
def initialize(controller)
|
|
14
|
+
@controller = controller.to_sym
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def matches?(request)
|
|
18
|
+
component = component_for(request.params[:component_id])
|
|
19
|
+
return false unless component
|
|
20
|
+
component.controller.to_sym == controller
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def component_for(component_slug)
|
|
26
|
+
self.class.components[component_slug] ||= Para::Component::Base.find_by_slug(component_slug)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Request based components cache
|
|
30
|
+
#
|
|
31
|
+
def self.components
|
|
32
|
+
RequestStore.store['para.components_by_id'] ||= {}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/para/routing.rb
ADDED
data/lib/para/sti/root_model.rb
CHANGED
|
@@ -16,8 +16,8 @@ module Para
|
|
|
16
16
|
|
|
17
17
|
module ClassMethods
|
|
18
18
|
def eager_load!
|
|
19
|
-
return if
|
|
20
|
-
|
|
19
|
+
return if RequestStore.store[descendants_eager_load_key]
|
|
20
|
+
RequestStore.store[descendants_eager_load_key] = true
|
|
21
21
|
|
|
22
22
|
models_dir = Rails.root.join('app', 'models')
|
|
23
23
|
|
|
@@ -38,6 +38,12 @@ module Para
|
|
|
38
38
|
|
|
39
39
|
private
|
|
40
40
|
|
|
41
|
+
def descendants_eager_load_key
|
|
42
|
+
@descendants_eager_load_key ||= [
|
|
43
|
+
'para', 'root_model', 'descendants_eager_loaded', name
|
|
44
|
+
].join(':')
|
|
45
|
+
end
|
|
46
|
+
|
|
41
47
|
# Allows the including class to define `.subclasses_namespace` class
|
|
42
48
|
# method to override the namespace and directory used to eager load the
|
|
43
49
|
# subclasses.
|
data/lib/para/version.rb
CHANGED