para 0.4.0
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +65 -0
- data/Rakefile +21 -0
- data/app/assets/images/para/admin/bg.png +0 -0
- data/app/assets/javascripts/admin/app.coffee +1 -0
- data/app/assets/javascripts/para/admin/table.coffee +46 -0
- data/app/assets/javascripts/para/admin/theme_actions.coffee +26 -0
- data/app/assets/javascripts/para/admin/tree.coffee +53 -0
- data/app/assets/javascripts/para/admin.coffee +18 -0
- data/app/assets/javascripts/para/application.js +13 -0
- data/app/assets/javascripts/para/inputs/nested_many.coffee +57 -0
- data/app/assets/javascripts/para/lib/ajax.coffee +11 -0
- data/app/assets/stylesheets/admin/app.sass +0 -0
- data/app/assets/stylesheets/admin/theme.sass +0 -0
- data/app/assets/stylesheets/para/admin/theme/_base.sass +170 -0
- data/app/assets/stylesheets/para/admin/theme/_breadcrumb.sass +29 -0
- data/app/assets/stylesheets/para/admin/theme/_buttons.sass +24 -0
- data/app/assets/stylesheets/para/admin/theme/_checkable.sass +107 -0
- data/app/assets/stylesheets/para/admin/theme/_commonds.sass +213 -0
- data/app/assets/stylesheets/para/admin/theme/_dropdown.sass +53 -0
- data/app/assets/stylesheets/para/admin/theme/_form.sass +141 -0
- data/app/assets/stylesheets/para/admin/theme/_list.sass +138 -0
- data/app/assets/stylesheets/para/admin/theme/_navigation.sass +287 -0
- data/app/assets/stylesheets/para/admin/theme/_navtabs.sass +127 -0
- data/app/assets/stylesheets/para/admin/theme/_orderable.sass +44 -0
- data/app/assets/stylesheets/para/admin/theme/_panel.sass +288 -0
- data/app/assets/stylesheets/para/admin/theme/_responsive.sass +123 -0
- data/app/assets/stylesheets/para/admin/theme/_sorting.sass +30 -0
- data/app/assets/stylesheets/para/admin/theme/_table.sass +24 -0
- data/app/assets/stylesheets/para/admin/theme/_tree.sass +74 -0
- data/app/assets/stylesheets/para/admin/theme/_variables.sass +31 -0
- data/app/assets/stylesheets/para/admin/theme.sass +22 -0
- data/app/assets/stylesheets/para/admin.sass +10 -0
- data/app/assets/stylesheets/para/application.css +15 -0
- data/app/assets/stylesheets/para/lib/_variables.scss +646 -0
- data/app/assets/stylesheets/para/overrides/datetimepicker.sass +17 -0
- data/app/assets/stylesheets/para/overrides/fuelux.sass +43 -0
- data/app/assets/stylesheets/para/overrides/jasny.bootstrap.sass +41 -0
- data/app/assets/stylesheets/para/overrides/redactor.sass +88 -0
- data/app/assets/stylesheets/para/overrides/responsive.sass +63 -0
- data/app/assets/stylesheets/para/overrides/selectize.sass +45 -0
- data/app/assets/stylesheets/para/overrides/slider.sass +13 -0
- data/app/assets/stylesheets/para/overrides/theme.sass +172 -0
- data/app/controllers/concerns/para/admin/resource_controller_concerns.rb +17 -0
- data/app/controllers/para/admin/base_controller.rb +46 -0
- data/app/controllers/para/admin/component_controller.rb +9 -0
- data/app/controllers/para/admin/crud_component_controller.rb +17 -0
- data/app/controllers/para/admin/crud_resources_controller.rb +71 -0
- data/app/controllers/para/admin/main_controller.rb +8 -0
- data/app/controllers/para/admin/resources_controller.rb +163 -0
- data/app/controllers/para/admin/settings_component_controller.rb +11 -0
- data/app/controllers/para/admin/settings_form_controller.rb +21 -0
- data/app/controllers/para/admin/singleton_resource_component_controller.rb +14 -0
- data/app/controllers/para/admin/singleton_resources_controller.rb +30 -0
- data/app/controllers/para/application_controller.rb +7 -0
- data/app/decorators/para/component/base_decorator.rb +48 -0
- data/app/decorators/para/component/crud_decorator.rb +32 -0
- data/app/decorators/para/component/settings_decorator.rb +11 -0
- data/app/decorators/para/component/singleton_resource_decorator.rb +11 -0
- data/app/decorators/para/component_section_decorator.rb +4 -0
- data/app/helpers/para/admin/base_helper.rb +57 -0
- data/app/helpers/para/admin/component_groups_helper.rb +4 -0
- data/app/helpers/para/admin/components_helper.rb +4 -0
- data/app/helpers/para/admin/resources_helper.rb +27 -0
- data/app/helpers/para/application_helper.rb +14 -0
- data/app/helpers/para/exports_helper.rb +11 -0
- data/app/helpers/para/flash_helper.rb +48 -0
- data/app/helpers/para/form_helper.rb +28 -0
- data/app/helpers/para/markup_helper.rb +15 -0
- data/app/helpers/para/model_helper.rb +38 -0
- data/app/helpers/para/navigation_helper.rb +8 -0
- data/app/helpers/para/ordering_helper.rb +20 -0
- data/app/helpers/para/search_helper.rb +22 -0
- data/app/helpers/para/tag_helper.rb +50 -0
- data/app/helpers/para/tree_helper.rb +41 -0
- data/app/models/para/component/base.rb +70 -0
- data/app/models/para/component/crud.rb +45 -0
- data/app/models/para/component/resource.rb +33 -0
- data/app/models/para/component/settings.rb +17 -0
- data/app/models/para/component/singleton_resource.rb +25 -0
- data/app/models/para/component_resource.rb +6 -0
- data/app/models/para/component_section.rb +18 -0
- data/app/views/layouts/para/admin.html.haml +32 -0
- data/app/views/layouts/para/application.html.erb +14 -0
- data/app/views/para/admin/component_sections/_form.html.haml +10 -0
- data/app/views/para/admin/component_sections/edit.html.haml +5 -0
- data/app/views/para/admin/component_sections/new.html.haml +5 -0
- data/app/views/para/admin/components/_form.html.haml +15 -0
- data/app/views/para/admin/components/new.html.haml +4 -0
- data/app/views/para/admin/crud_component/show.html.haml +4 -0
- data/app/views/para/admin/dashboard.html.haml +10 -0
- data/app/views/para/admin/main/index.html.haml +1 -0
- data/app/views/para/admin/resources/_exports_menu.html.haml +12 -0
- data/app/views/para/admin/resources/_fields.html.haml +5 -0
- data/app/views/para/admin/resources/_filters.html.haml +9 -0
- data/app/views/para/admin/resources/_form.html.haml +6 -0
- data/app/views/para/admin/resources/_list.html.haml +22 -0
- data/app/views/para/admin/resources/_table.html.haml +8 -0
- data/app/views/para/admin/resources/_tree.html.haml +15 -0
- data/app/views/para/admin/resources/_tree_item.html.haml +16 -0
- data/app/views/para/admin/resources/edit.html.haml +6 -0
- data/app/views/para/admin/resources/new.html.haml +6 -0
- data/app/views/para/admin/settings_component/show.html.haml +12 -0
- data/app/views/para/admin/shared/_breadcrumb.html.haml +3 -0
- data/app/views/para/admin/shared/_header.html.haml +27 -0
- data/app/views/para/admin/shared/_navigation.html.haml +21 -0
- data/app/views/para/admin/singleton_resource_component/show.html.haml +5 -0
- data/app/views/para/inputs/_nested_many.html.haml +10 -0
- data/app/views/para/inputs/_nested_many_container.html.haml +19 -0
- data/app/views/para/inputs/_nested_one.html.haml +3 -0
- data/config/locales/en.yml +86 -0
- data/config/locales/fr.yml +101 -0
- data/db/migrate/20140911091225_create_para_components.rb +19 -0
- data/db/migrate/20140911112150_add_slug_to_para_components.rb +6 -0
- data/db/migrate/20140929125733_create_para_component_sections.rb +8 -0
- data/db/migrate/20140929131111_add_identifier_to_para_component_sections.rb +5 -0
- data/db/migrate/20140930121822_add_position_to_para_component_section.rb +5 -0
- data/db/migrate/20150129170710_create_para_component_resources.rb +13 -0
- data/db/migrate/20150203173219_add_identifier_to_para_components.rb +5 -0
- data/lib/generators/para/admin_user/admin_user_generator.rb +16 -0
- data/lib/generators/para/component/component_generator.rb +59 -0
- data/lib/generators/para/component/templates/component.rb +3 -0
- data/lib/generators/para/component/templates/component_controller.rb +7 -0
- data/lib/generators/para/component/templates/show.html.haml +2 -0
- data/lib/generators/para/exporter/exporter_generator.rb +42 -0
- data/lib/generators/para/exporter/templates/base_exporter.rb +15 -0
- data/lib/generators/para/exporter/templates/csv_exporter.rb +13 -0
- data/lib/generators/para/form/form_generator.rb +15 -0
- data/lib/generators/para/form/templates/_form.html.haml +7 -0
- data/lib/generators/para/install/install_generator.rb +112 -0
- data/lib/generators/para/install/templates/components.rb +13 -0
- data/lib/generators/para/install/templates/initializer.rb +23 -0
- data/lib/generators/para/nested_fields/nested_fields_generator.rb +15 -0
- data/lib/generators/para/nested_fields/templates/_nested_fields.html.haml +3 -0
- data/lib/generators/para/orderable/orderable_generator.rb +53 -0
- data/lib/generators/para/orderable/templates/orderable_migration.rb +6 -0
- data/lib/generators/para/resource/resource_generator.rb +71 -0
- data/lib/generators/para/resource/templates/resource_controller.rb +5 -0
- data/lib/generators/para/table/table_generator.rb +34 -0
- data/lib/generators/para/table/templates/_table.html.haml +10 -0
- data/lib/para/attribute_field/base.rb +77 -0
- data/lib/para/attribute_field/belongs_to.rb +29 -0
- data/lib/para/attribute_field/boolean.rb +15 -0
- data/lib/para/attribute_field/datetime.rb +9 -0
- data/lib/para/attribute_field/file.rb +20 -0
- data/lib/para/attribute_field/has_many.rb +31 -0
- data/lib/para/attribute_field/image.rb +25 -0
- data/lib/para/attribute_field/nested_many.rb +26 -0
- data/lib/para/attribute_field/nested_one.rb +27 -0
- data/lib/para/attribute_field/password.rb +18 -0
- data/lib/para/attribute_field/redactor.rb +20 -0
- data/lib/para/attribute_field/relation.rb +52 -0
- data/lib/para/attribute_field/translation.rb +9 -0
- data/lib/para/attribute_field_mappings.rb +82 -0
- data/lib/para/cloneable.rb +20 -0
- data/lib/para/component/exportable.rb +25 -0
- data/lib/para/component.rb +27 -0
- data/lib/para/components_configuration.rb +156 -0
- data/lib/para/config.rb +21 -0
- data/lib/para/engine.rb +59 -0
- data/lib/para/errors.rb +15 -0
- data/lib/para/exporter/base.rb +27 -0
- data/lib/para/exporter/csv.rb +49 -0
- data/lib/para/exporter.rb +57 -0
- data/lib/para/ext/cancan.rb +26 -0
- data/lib/para/ext/paperclip.rb +67 -0
- data/lib/para/ext.rb +13 -0
- data/lib/para/form_builder/containers.rb +70 -0
- data/lib/para/form_builder/field_mappings.rb +9 -0
- data/lib/para/form_builder/nested_form.rb +80 -0
- data/lib/para/form_builder/ordering.rb +15 -0
- data/lib/para/form_builder/settings.rb +24 -0
- data/lib/para/form_builder.rb +22 -0
- data/lib/para/generators/field_helpers.rb +32 -0
- data/lib/para/generators/name_helpers.rb +9 -0
- data/lib/para/generators/named_base.rb +7 -0
- data/lib/para/generators.rb +11 -0
- data/lib/para/inputs/nested_many_input.rb +40 -0
- data/lib/para/inputs/nested_one_input.rb +24 -0
- data/lib/para/inputs.rb +11 -0
- data/lib/para/markup/alert.rb +37 -0
- data/lib/para/markup/component.rb +20 -0
- data/lib/para/markup/modal.rb +52 -0
- data/lib/para/markup/panel.rb +41 -0
- data/lib/para/markup/resources_table.rb +178 -0
- data/lib/para/markup.rb +10 -0
- data/lib/para/model_field_parsers/base.rb +20 -0
- data/lib/para/model_field_parsers/devise.rb +37 -0
- data/lib/para/model_field_parsers/globalize.rb +23 -0
- data/lib/para/model_field_parsers/orderable.rb +15 -0
- data/lib/para/model_field_parsers/paperclip.rb +51 -0
- data/lib/para/model_field_parsers/redactor.rb +19 -0
- data/lib/para/model_field_parsers/relations.rb +67 -0
- data/lib/para/model_field_parsers.rb +22 -0
- data/lib/para/orderable.rb +44 -0
- data/lib/para/routes.rb +49 -0
- data/lib/para/version.rb +3 -0
- data/lib/para.rb +65 -0
- data/lib/rails/relation_length_validator.rb +45 -0
- data/lib/rails/routing_mapper.rb +27 -0
- data/lib/tasks/para_tasks.rake +22 -0
- metadata +595 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module Component
|
|
3
|
+
def self.registered_components
|
|
4
|
+
@registered_components ||= {}
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def self.registered_component?(model_name)
|
|
8
|
+
registered_components.any? do |name, component|
|
|
9
|
+
component.name == model_name
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.config
|
|
14
|
+
@config ||= Para::ComponentsConfiguration.new
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Require concerns
|
|
20
|
+
require 'para/component/exportable'
|
|
21
|
+
|
|
22
|
+
# Require models
|
|
23
|
+
require 'para/component/base'
|
|
24
|
+
require 'para/component/resource'
|
|
25
|
+
require 'para/component/crud'
|
|
26
|
+
require 'para/component/singleton_resource'
|
|
27
|
+
require 'para/component/settings'
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
class ComponentsConfiguration
|
|
3
|
+
def draw(&block)
|
|
4
|
+
return unless components_installed?
|
|
5
|
+
eager_load_components!
|
|
6
|
+
instance_eval(&block)
|
|
7
|
+
build
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def section(*args, &block)
|
|
11
|
+
sections << Section.new(*args, &block)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def sections
|
|
15
|
+
@sections ||= []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def method_missing(method, *args, &block)
|
|
19
|
+
component_for(method) || super
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def section_for(identifier)
|
|
23
|
+
if (section = sections_cache[identifier])
|
|
24
|
+
section
|
|
25
|
+
elsif (section_id = sections_ids_hash[identifier])
|
|
26
|
+
sections_cache[identifier] = Para::ComponentSection.find(section_id)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def component_for(identifier)
|
|
31
|
+
if (component = components_cache[identifier])
|
|
32
|
+
component
|
|
33
|
+
elsif (component_id = components_ids_hash[identifier])
|
|
34
|
+
components_cache[identifier] = Para::Component::Base.find(component_id)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def build
|
|
41
|
+
sections.each_with_index do |section, index|
|
|
42
|
+
section.refresh(position: index)
|
|
43
|
+
sections_ids_hash[section.identifier] = section.model.id
|
|
44
|
+
|
|
45
|
+
section.components.each do |component|
|
|
46
|
+
components_ids_hash[component.identifier] = component.model.id
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def sections_ids_hash
|
|
52
|
+
@sections_ids_hash ||= {}.with_indifferent_access
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def components_ids_hash
|
|
56
|
+
@components_ids_hash ||= {}.with_indifferent_access
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Only store sections cache for the request duration to avoid expired
|
|
60
|
+
# references to AR objects between requests
|
|
61
|
+
#
|
|
62
|
+
def sections_cache
|
|
63
|
+
RequestStore.store[:sections_cache] ||= {}.with_indifferent_access
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Only store components cache for the request duration to avoid expired
|
|
67
|
+
# references to AR objects between requests
|
|
68
|
+
#
|
|
69
|
+
def components_cache
|
|
70
|
+
RequestStore.store[:components_cache] ||= {}.with_indifferent_access
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def components_installed?
|
|
74
|
+
tables_exist = %w(component/base component_section).all? do |name|
|
|
75
|
+
Para.const_get(name.camelize).table_exists?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
unless tables_exist
|
|
79
|
+
Rails.logger.warn(
|
|
80
|
+
"Para migrations are not installed.\n" \
|
|
81
|
+
"Skipping components definition until next app reload."
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
tables_exist
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def eager_load_components!
|
|
89
|
+
$LOAD_PATH.each do |path|
|
|
90
|
+
next unless path.match(/\/components$/)
|
|
91
|
+
|
|
92
|
+
glob = File.join(path, '**', '*_component.rb')
|
|
93
|
+
|
|
94
|
+
Dir[glob].each do |file|
|
|
95
|
+
load(file)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
class Section
|
|
101
|
+
attr_accessor :identifier, :model
|
|
102
|
+
|
|
103
|
+
def initialize(identifier, &block)
|
|
104
|
+
self.identifier = identifier.to_s
|
|
105
|
+
instance_eval(&block)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def component(*args)
|
|
109
|
+
components << Component.new(*args)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def components
|
|
113
|
+
@components ||= []
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def refresh(attributes = {})
|
|
117
|
+
self.model = ComponentSection.where(identifier: identifier).first_or_initialize
|
|
118
|
+
model.assign_attributes(attributes)
|
|
119
|
+
model.save!
|
|
120
|
+
|
|
121
|
+
components.each_with_index do |component, index|
|
|
122
|
+
component.refresh(component_section: model, position: index)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
class Component
|
|
128
|
+
attr_accessor :identifier, :type, :options, :model
|
|
129
|
+
|
|
130
|
+
def initialize(identifier, type, options = {})
|
|
131
|
+
self.identifier = identifier.to_s
|
|
132
|
+
self.type = Para::Component.registered_components[type]
|
|
133
|
+
self.options = options
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def refresh(attributes = {})
|
|
137
|
+
self.model = type.where(identifier: identifier).first_or_initialize
|
|
138
|
+
model.assign_attributes(attributes.merge(options_with_defaults))
|
|
139
|
+
model.save!
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Ensures unset :configuration store options are set to nil to allow
|
|
143
|
+
# removing a configuration option from the components.rb file
|
|
144
|
+
#
|
|
145
|
+
def options_with_defaults
|
|
146
|
+
configurable_keys = type.local_stored_attributes.try(:[], :configuration) || []
|
|
147
|
+
configurable_keys += options.keys
|
|
148
|
+
configurable_keys.uniq!
|
|
149
|
+
|
|
150
|
+
configurable_keys.each_with_object({}) do |key, hash|
|
|
151
|
+
hash[key] = options[key]
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
data/lib/para/config.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module Config
|
|
3
|
+
mattr_accessor :authenticate_admin_method
|
|
4
|
+
@@authenticate_admin_method = :authenticate_admin_user!
|
|
5
|
+
|
|
6
|
+
mattr_accessor :current_admin_method
|
|
7
|
+
@@current_admin_method = :current_admin_user
|
|
8
|
+
|
|
9
|
+
mattr_accessor :pagination_theme
|
|
10
|
+
@@pagination_theme = 'twitter-bootstrap-3'
|
|
11
|
+
|
|
12
|
+
mattr_accessor :admin_title
|
|
13
|
+
@@admin_title = nil
|
|
14
|
+
|
|
15
|
+
mattr_accessor :default_tree_max_depth
|
|
16
|
+
@@default_tree_max_depth = 3
|
|
17
|
+
|
|
18
|
+
mattr_accessor :resource_name_methods
|
|
19
|
+
@@resource_name_methods = [:name, :title]
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/para/engine.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
initializer 'add vendor path to assets pipeline' do |app|
|
|
4
|
+
%w(javascripts stylesheets).each do |folder|
|
|
5
|
+
app.config.assets.paths << File.expand_path(
|
|
6
|
+
"../../../vendor/assets/#{ folder }",
|
|
7
|
+
__FILE__
|
|
8
|
+
)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
initializer 'Para precompile hook', group: :all do |app|
|
|
13
|
+
app.config.assets.precompile += %w(
|
|
14
|
+
para/admin.js
|
|
15
|
+
para/admin.css
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
initializer 'Para Orderable Mixin' do
|
|
20
|
+
ActiveSupport.on_load(:active_record) do
|
|
21
|
+
include Para::ActiveRecordOrderableMixin
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
initializer 'Para Cloneable' do
|
|
26
|
+
ActiveSupport.on_load(:active_record) do
|
|
27
|
+
include Para::Cloneable
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
initializer 'Extend paperclip attachment definition' do
|
|
32
|
+
return unless Kernel.const_defined?('Paperclip')
|
|
33
|
+
|
|
34
|
+
ActiveSupport.on_load(:active_record) do
|
|
35
|
+
::Paperclip::HasAttachedFile.send(
|
|
36
|
+
:include, Para::Ext::Paperclip::HasAttachedFileMixin
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
initializer 'Extend cancan ControllerResource class' do
|
|
42
|
+
return unless Kernel.const_defined?('CanCan')
|
|
43
|
+
|
|
44
|
+
ActiveSupport.on_load(:active_record) do
|
|
45
|
+
::CanCan::ControllerResource.send(
|
|
46
|
+
:include, Para::Ext::Cancan::ControllerResource
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
initializer 'Build components tree' do |app|
|
|
52
|
+
components_config_path = Rails.root.join('config', 'components.rb')
|
|
53
|
+
|
|
54
|
+
app.config.to_prepare do
|
|
55
|
+
require components_config_path if File.exist?(components_config_path)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/para/errors.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
class BaseException < StandardError; end
|
|
3
|
+
|
|
4
|
+
class ComponentNotFound < BaseException
|
|
5
|
+
attr_accessor :type
|
|
6
|
+
|
|
7
|
+
def initialize(type)
|
|
8
|
+
@type = type
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def message
|
|
12
|
+
"Component not found for type : #{ type }"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module Exporter
|
|
3
|
+
class Base
|
|
4
|
+
attr_reader :resources
|
|
5
|
+
|
|
6
|
+
def initialize(resources)
|
|
7
|
+
@resources = resources
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def disposition
|
|
11
|
+
'inline'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def extension
|
|
15
|
+
raise '#extension must be defined to create the export file name'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def file_name
|
|
19
|
+
@file_name ||= [name, extension].join('.')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.register_base_exporter(type, exporter)
|
|
23
|
+
Exporter.base_exporters[type] = exporter
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
|
|
3
|
+
module Para
|
|
4
|
+
module Exporter
|
|
5
|
+
class Csv < Base
|
|
6
|
+
register_base_exporter :csv, self
|
|
7
|
+
|
|
8
|
+
def extension
|
|
9
|
+
'csv'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def mime_type
|
|
13
|
+
'text/csv'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def export_type
|
|
17
|
+
:excel
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render
|
|
21
|
+
CSV.generate do |csv|
|
|
22
|
+
csv << headers
|
|
23
|
+
|
|
24
|
+
resources.each do |resource|
|
|
25
|
+
csv << row_for(resource)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def headers
|
|
33
|
+
fields.map do |field|
|
|
34
|
+
encode(User.human_attribute_name(field))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def row_for(resource)
|
|
39
|
+
fields.map do |field|
|
|
40
|
+
encode(resource.send(field))
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def encode(string)
|
|
45
|
+
string.presence && string.to_s.encode('UTF-8')
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module Exporter
|
|
3
|
+
class MissingExporterError < StandardError
|
|
4
|
+
attr_accessor :model_name, :format, :exporter_name
|
|
5
|
+
|
|
6
|
+
def initialize(model_name, format, exporter_name)
|
|
7
|
+
@model_name = model_name
|
|
8
|
+
@format = format
|
|
9
|
+
@exporter_name = exporter_name
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def message
|
|
13
|
+
"No exporter found for model \"#{ model_name }\" and format " +
|
|
14
|
+
"\"#{ format }\". Please create the #{ exporter_name } class " +
|
|
15
|
+
"manually or with the following command : " +
|
|
16
|
+
"`rails g para:exporter #{ model_name.underscore } #{ format }"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.for(model_name, format)
|
|
21
|
+
exporter_name = name_for(model_name, format)
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
const_get(exporter_name)
|
|
25
|
+
rescue NameError => e
|
|
26
|
+
if e.message == "uninitialized constant Para::Exporter::#{ exporter_name }"
|
|
27
|
+
raise MissingExporterError.new(model_name, format, exporter_name)
|
|
28
|
+
else
|
|
29
|
+
raise e
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.name_for(model_name, format)
|
|
35
|
+
[
|
|
36
|
+
'',
|
|
37
|
+
format_exporter_name(format),
|
|
38
|
+
model_exporter_name(model_name)
|
|
39
|
+
].join('::')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.model_exporter_name(model_name)
|
|
43
|
+
[model_name.to_s.pluralize, 'Exporter'].join
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.format_exporter_name(format)
|
|
47
|
+
format.to_s.camelize
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.base_exporters
|
|
51
|
+
@base_exporters ||= {}.with_indifferent_access
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
require 'para/exporter/base'
|
|
57
|
+
require 'para/exporter/csv'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module Ext
|
|
3
|
+
module Cancan
|
|
4
|
+
module ControllerResource
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
alias_method_chain :assign_attributes, :parent_missing_management
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Todo : Document why this extension was added ?
|
|
12
|
+
def assign_attributes_with_parent_missing_management(resource)
|
|
13
|
+
if @options[:singleton] && parent_resource && resource.respond_to?(:"#{ parent_name }=")
|
|
14
|
+
resource.send(:"#{ parent_name }=", parent_resource)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
initial_attributes.each do |attr_name, value|
|
|
18
|
+
resource.send(:"#{ attr_name }=", value)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
resource
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module Ext
|
|
3
|
+
module Paperclip
|
|
4
|
+
module HasAttachedFileMixin
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
alias_method_chain :define, :removeable_management
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def define_with_removeable_management
|
|
12
|
+
define_without_removeable_management
|
|
13
|
+
define_removeable
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def define_removeable
|
|
19
|
+
return if @options[:removeable] == false
|
|
20
|
+
|
|
21
|
+
attachment_name = @name
|
|
22
|
+
klass = @klass
|
|
23
|
+
# Define the setter to remove the attachment
|
|
24
|
+
#
|
|
25
|
+
@klass.send :define_method, :"remove_#{ attachment_name }=" do |value|
|
|
26
|
+
if value == '1'
|
|
27
|
+
removed_attachments << attachment_name
|
|
28
|
+
|
|
29
|
+
# Notify ActiveRecord that the model has changed so nested models
|
|
30
|
+
# get to run validation hooks and attachments are cleared
|
|
31
|
+
send(:"#{ attachment_name }_file_name_will_change!")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
@klass.send :define_method, :"remove_#{ attachment_name }" do |value|
|
|
36
|
+
removed_attachments.include?(attachment_name) ? '1' : nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Lazy method initialization on attachment target class
|
|
40
|
+
#
|
|
41
|
+
unless @klass.method_defined?(:removed_attachments)
|
|
42
|
+
# List of all removed attachments for the current instance
|
|
43
|
+
#
|
|
44
|
+
@klass.send :define_method, :removed_attachments do
|
|
45
|
+
@removed_attachments ||= []
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Define before validation hook to clear removed attachments before
|
|
49
|
+
# the instance is validated
|
|
50
|
+
#
|
|
51
|
+
@klass.send :define_method, :clear_removed_attachments do
|
|
52
|
+
removed_attachments.each do |name|
|
|
53
|
+
if send(:"#{ name }?")
|
|
54
|
+
send(name).clear
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
removed_attachments.clear
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
@klass.send(:before_validation, :clear_removed_attachments)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
data/lib/para/ext.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
# References all framework extensions to make our code work well with other
|
|
3
|
+
# libraries.
|
|
4
|
+
#
|
|
5
|
+
# One goal is to have the minimum of them, but sometimes, without them, we
|
|
6
|
+
# may need to make our own code dirty
|
|
7
|
+
#
|
|
8
|
+
module Ext
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require 'para/ext/cancan'
|
|
13
|
+
require 'para/ext/paperclip'
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module FormBuilder
|
|
3
|
+
module Containers
|
|
4
|
+
def fieldset(&block)
|
|
5
|
+
template.content_tag(:div, class: 'block form-inputs') do
|
|
6
|
+
template.capture(&block)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def actions(options = {}, &block)
|
|
11
|
+
template.content_tag(:div, class: 'block form-actions form-group') do
|
|
12
|
+
template.content_tag(:div, class: 'col-sm-9 col-sm-offset-3') do
|
|
13
|
+
if block
|
|
14
|
+
template.capture(&block)
|
|
15
|
+
else
|
|
16
|
+
if options.empty?
|
|
17
|
+
options[:only] = template.instance_variable_get(:@component).default_form_actions
|
|
18
|
+
end
|
|
19
|
+
actions_buttons_for(options).join("\n").html_safe
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def actions_buttons_for(options)
|
|
26
|
+
names = [:submit, :submit_and_edit, :submit_and_add_another, :cancel]
|
|
27
|
+
|
|
28
|
+
names.select! { |name| Array.wrap(options[:only]).include?(name) } if options[:only]
|
|
29
|
+
names.reject! { |name| Array.wrap(options[:except]).include?(name) } if options[:except]
|
|
30
|
+
buttons = names.map { |name| send(:"para_#{ name }_button") }
|
|
31
|
+
buttons.unshift(return_to_hidden_field)
|
|
32
|
+
buttons
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def return_to_hidden_field
|
|
36
|
+
template.hidden_field_tag(:return_to, return_to_path)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def para_submit_button(options = {})
|
|
40
|
+
button(:submit, I18n.t('para.shared.save'), name: '_save', class: 'btn-success')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def para_submit_and_edit_button
|
|
44
|
+
button(:submit, I18n.t('para.shared.save_and_edit'), name: '_save_and_edit', class: 'btn-primary')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def para_submit_and_add_another_button
|
|
48
|
+
button(:submit, I18n.t('para.shared.save_and_add_another_button'), name: '_save_and_add_another', class: 'btn-primary')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def para_cancel_button
|
|
52
|
+
template.link_to(
|
|
53
|
+
I18n.t('para.shared.cancel'),
|
|
54
|
+
return_to_path,
|
|
55
|
+
class: 'btn btn-danger'
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def return_to_path
|
|
60
|
+
template.params[:return_to].presence || component_path
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def component_path
|
|
64
|
+
if (component = template.instance_variable_get(:@component))
|
|
65
|
+
component.path
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module FormBuilder
|
|
3
|
+
module NestedForm
|
|
4
|
+
def nested_fields
|
|
5
|
+
@nested_fields ||= fields.reject do |field|
|
|
6
|
+
inverse_of?(field.name)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def nested_resource_name
|
|
11
|
+
@nested_resource_name ||= begin
|
|
12
|
+
name_method = Para.config.resource_name_methods.find do |method_name|
|
|
13
|
+
object.respond_to?(method_name)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
(name_method && object.try(name_method).presence) || default_resource_name
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def nested_resource_dom_id
|
|
21
|
+
return "" unless nested?
|
|
22
|
+
|
|
23
|
+
@nested_resource_dom_id ||= [
|
|
24
|
+
object.class.model_name.singular,
|
|
25
|
+
(Time.now.to_f * 1000).to_i,
|
|
26
|
+
(object.id || "_new_#{ nested_attribute_name }_id")
|
|
27
|
+
].join('-')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def remove_association_button
|
|
31
|
+
return "" unless allow_destroy?
|
|
32
|
+
|
|
33
|
+
template.content_tag(:div, class: 'panel-btns pull-right') do
|
|
34
|
+
template.link_to_remove_association(
|
|
35
|
+
self, wrapper_class: 'form-fields', class: 'btn btn-danger'
|
|
36
|
+
) do
|
|
37
|
+
template.content_tag(:i, '', class: 'fa fa-trash') +
|
|
38
|
+
I18n.t('para.form.nested.remove')
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def allow_destroy?
|
|
44
|
+
return false unless nested?
|
|
45
|
+
|
|
46
|
+
nested_options = parent_object.nested_attributes_options
|
|
47
|
+
relation = nested_options[nested_attribute_name]
|
|
48
|
+
relation && relation[:allow_destroy]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def inverse_of?(field_name)
|
|
52
|
+
return false unless nested?
|
|
53
|
+
|
|
54
|
+
reflection = parent_object.class.reflect_on_association(nested_attribute_name)
|
|
55
|
+
reflection && (reflection.options[:inverse_of] == field_name)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def nested?
|
|
59
|
+
nested_attribute_name.present?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def nested_attribute_name
|
|
63
|
+
options[:nested_attribute_name]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def parent_object
|
|
67
|
+
nested? && options[:parent_builder] && options[:parent_builder].object
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def default_resource_name
|
|
73
|
+
model_name = object.class.model_name.human
|
|
74
|
+
id_or_new = (id = object.id) ? id : I18n.t('para.form.nested.new')
|
|
75
|
+
|
|
76
|
+
[model_name, id_or_new].join(' - ')
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module FormBuilder
|
|
3
|
+
module Ordering
|
|
4
|
+
def orderable?
|
|
5
|
+
options.fetch(:orderable, object.class.orderable?)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def reorder_anchor(options = {})
|
|
9
|
+
return "" unless orderable?
|
|
10
|
+
options[:form] = self
|
|
11
|
+
template.reorder_anchor(options)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|