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,53 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
class OrderableGenerator < Rails::Generators::NamedBase
|
|
3
|
+
include Rails::Generators::Migration
|
|
4
|
+
|
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
6
|
+
|
|
7
|
+
class_option :migrate, type: :boolean, default: false, :aliases => "-m"
|
|
8
|
+
|
|
9
|
+
desc 'Para orderable model updater'
|
|
10
|
+
|
|
11
|
+
def welcome
|
|
12
|
+
say "Making #{ class_name } model orderable ..."
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add_field_to_model
|
|
16
|
+
migration_template(
|
|
17
|
+
'orderable_migration.rb',
|
|
18
|
+
"db/migrate/add_orderable_position_to_#{ plural_file_name }.rb"
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_orderable_to_model
|
|
23
|
+
class_definition = "class #{ class_name } < ActiveRecord::Base\n"
|
|
24
|
+
|
|
25
|
+
inject_into_file "app/models/#{ file_name }.rb", after: class_definition do
|
|
26
|
+
" acts_as_orderable\n"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def migrate
|
|
31
|
+
rake 'db:migrate' if options[:migrate]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def fianl_message
|
|
35
|
+
message = "The #{ class_name } model is now orderable.\n"
|
|
36
|
+
message << "* Please migrate to update your model's table\n" unless options[:migrate]
|
|
37
|
+
message << <<-MESSAGE
|
|
38
|
+
* Please add to your model resource's routes the ordering path.
|
|
39
|
+
e.g.: resources :#{ plural_file_name } do
|
|
40
|
+
collection do
|
|
41
|
+
patch 'order'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
MESSAGE
|
|
45
|
+
|
|
46
|
+
say(message)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.next_migration_number(dir)
|
|
50
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
class ResourceGenerator < Rails::Generators::NamedBase
|
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
4
|
+
|
|
5
|
+
argument :component_name, type: :string
|
|
6
|
+
argument :attributes, type: :array
|
|
7
|
+
|
|
8
|
+
class_option :migrate, type: :boolean, default: false, :aliases => "-m"
|
|
9
|
+
class_option :orderable, type: :boolean, default: false, :aliases => "-o"
|
|
10
|
+
|
|
11
|
+
desc 'Para resource generator'
|
|
12
|
+
|
|
13
|
+
def welcome
|
|
14
|
+
say 'Creating resource...'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def copy_resource_controller
|
|
18
|
+
template 'resource_controller.rb', "app/controllers/admin/#{ plural_file_name }_controller.rb"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_resource_to_component_controller
|
|
22
|
+
gsub_file "app/controllers/admin/#{ component_name.singularize }_component_controller.rb", /# You can access @component here/ do
|
|
23
|
+
<<-RUBY
|
|
24
|
+
@q = @component.#{ plural_file_name }.search(params[:q])
|
|
25
|
+
@resources = @q.result.page(params[:page])
|
|
26
|
+
RUBY
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def insert_route
|
|
31
|
+
inject_into_file 'config/routes.rb', after: "component :#{ component_name.underscore } do" do
|
|
32
|
+
"\n resources :#{ plural_file_name }"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def insert_relation_to_show
|
|
37
|
+
append_to_file "app/views/admin/#{ component_name.underscore }_component/show.html.haml" do
|
|
38
|
+
"\n\n= listing_for(@resources)"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def generate_model
|
|
43
|
+
generate 'model',
|
|
44
|
+
file_name,
|
|
45
|
+
attributes.map { |attr|
|
|
46
|
+
"#{ attr.name }:#{ attr.type }"
|
|
47
|
+
}.insert(-1, 'component:references').join(' ')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def orderable
|
|
51
|
+
generate 'para:orderable', file_name if options[:orderable]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def migrate
|
|
55
|
+
rake 'db:migrate' if options[:migrate]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def insert_belongs_to_to_resource
|
|
59
|
+
inject_into_file "app/models/#{ file_name }.rb", after: "belongs_to :component" do
|
|
60
|
+
", class_name: 'Para::Component::Base'"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def insert_has_many_to_component
|
|
65
|
+
inject_into_file "app/components/#{ component_name.underscore }_component.rb", after: "register :#{ component_name.underscore }, self" do
|
|
66
|
+
"\n\n has_many :#{ plural_file_name }, class_name: '::#{ class_name }', inverse_of: :component,
|
|
67
|
+
foreign_key: :component_id, dependent: :destroy"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
class TableGenerator < Para::Generators::NamedBase
|
|
3
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
4
|
+
|
|
5
|
+
desc 'Para resources table generator'
|
|
6
|
+
|
|
7
|
+
def generate_table
|
|
8
|
+
template(
|
|
9
|
+
"_table.html.haml",
|
|
10
|
+
"app/views/admin/#{ plural_namespaced_path }/_table.haml"
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def attributes
|
|
17
|
+
@attributes ||= begin
|
|
18
|
+
model =
|
|
19
|
+
begin
|
|
20
|
+
Para.const_get(class_name)
|
|
21
|
+
rescue
|
|
22
|
+
class_name.classify.constantize
|
|
23
|
+
end
|
|
24
|
+
AttributeFieldMappings.new(model).fields
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def attributes_list
|
|
29
|
+
@attributes_list ||= attributes.map do |field|
|
|
30
|
+
field.name.to_sym.inspect
|
|
31
|
+
end.join(', ')
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
= resources_table(model: model, component: component) do |table|
|
|
2
|
+
= table.header do
|
|
3
|
+
<%- attributes.each do |field| -%>
|
|
4
|
+
= table.header_for(:<%= field.name %>)
|
|
5
|
+
<%- end -%>
|
|
6
|
+
|
|
7
|
+
= table.rows(resources) do |resource|
|
|
8
|
+
<%- attributes.each do |field| -%>
|
|
9
|
+
= table.data_for(resource, :<%= field.name %>)
|
|
10
|
+
<%- end -%>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class Base
|
|
4
|
+
class_attribute :_field_options
|
|
5
|
+
|
|
6
|
+
attr_reader :model, :name, :type, :field_type, :field_method
|
|
7
|
+
|
|
8
|
+
def self.field_option(key, method_name, options = {})
|
|
9
|
+
self._field_options ||= []
|
|
10
|
+
|
|
11
|
+
self._field_options += [{
|
|
12
|
+
key: key,
|
|
13
|
+
method_name: method_name,
|
|
14
|
+
options: options
|
|
15
|
+
}]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
field_option :as, :field_type_name
|
|
19
|
+
|
|
20
|
+
def initialize(model, options = {})
|
|
21
|
+
@model = model
|
|
22
|
+
@name = options[:name]
|
|
23
|
+
@type = options[:type]
|
|
24
|
+
@field_type = options[:field_type]
|
|
25
|
+
|
|
26
|
+
determine_name_and_field_method!
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def determine_name_and_field_method!
|
|
30
|
+
name = @name
|
|
31
|
+
|
|
32
|
+
reference = model.reflect_on_all_associations.find do |association|
|
|
33
|
+
association.foreign_key == name
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if reference
|
|
37
|
+
@name = reference.name
|
|
38
|
+
@field_method = :association
|
|
39
|
+
else
|
|
40
|
+
@name = name
|
|
41
|
+
@field_method = :input
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def value_for(instance)
|
|
46
|
+
instance.send(name)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Allows parsing input params before they're passed to the model, so
|
|
50
|
+
# it can be easy to edit them according to some field type specific
|
|
51
|
+
# behavior
|
|
52
|
+
#
|
|
53
|
+
def parse_input(params); end
|
|
54
|
+
|
|
55
|
+
def field_options
|
|
56
|
+
self.class._field_options.each_with_object({}) do |params, hash|
|
|
57
|
+
value = send(params[:method_name])
|
|
58
|
+
hash[params[:key]] = value if value != nil || params[:options][:allow_nil]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def field_name
|
|
63
|
+
name
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def attribute_column_path
|
|
67
|
+
[name]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def field_type_name
|
|
73
|
+
field_type.presence && field_type.to_sym
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class BelongsToField < RelationField
|
|
4
|
+
field_option :collection, :relation_options
|
|
5
|
+
|
|
6
|
+
def field_name
|
|
7
|
+
reflection.name
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def value_for(instance)
|
|
11
|
+
if (resource = instance.send(name))
|
|
12
|
+
resource_name(resource)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def relation_options
|
|
17
|
+
reflection.klass.all
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parse_input(params)
|
|
21
|
+
if (id = params[reflection.foreign_key].presence) && !reflection.klass.exists?(id: id)
|
|
22
|
+
on_the_fly_creation(id) do |resource|
|
|
23
|
+
params[reflection.foreign_key] = resource.id
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class BooleanField < AttributeField::Base
|
|
4
|
+
field_option :wrapper, :wrapper_name
|
|
5
|
+
|
|
6
|
+
def value_for(instance)
|
|
7
|
+
I18n.t("para.types.boolean.#{ (!!instance.send(name)).to_s }")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def wrapper_name
|
|
11
|
+
:horizontal_radio_and_checkboxes
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class FileField < AttributeField::Base
|
|
4
|
+
include ActionView::Helpers::UrlHelper
|
|
5
|
+
|
|
6
|
+
field_option :wrapper, :wrapper_name
|
|
7
|
+
|
|
8
|
+
def value_for(instance)
|
|
9
|
+
if instance.send(:"#{ name }?")
|
|
10
|
+
url = instance.send(name).url
|
|
11
|
+
link_to(url, url)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def wrapper_name
|
|
16
|
+
:horizontal_file_input
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class HasManyField < RelationField
|
|
4
|
+
def field_name
|
|
5
|
+
reflection.name
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def value_for(instance)
|
|
9
|
+
instance.send(name).map do |resource|
|
|
10
|
+
resource_name(resource)
|
|
11
|
+
end.join(', ')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def parse_input(params)
|
|
15
|
+
if (ids = params[plural_foreign_key].presence) && String === ids
|
|
16
|
+
# Format selectize value for Rails
|
|
17
|
+
ids = params[plural_foreign_key] = ids.split(',')
|
|
18
|
+
|
|
19
|
+
on_the_fly_creation(ids) do |resource, value|
|
|
20
|
+
params[plural_foreign_key].delete(value)
|
|
21
|
+
params[plural_foreign_key] << resource.id
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def plural_foreign_key
|
|
27
|
+
foreign_key.to_s.pluralize
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class ImageField < AttributeField::Base
|
|
4
|
+
include ActionView::Helpers::AssetTagHelper
|
|
5
|
+
|
|
6
|
+
def value_for(instance)
|
|
7
|
+
style = attachment_thumb_style_for(instance)
|
|
8
|
+
|
|
9
|
+
if instance.send(:"#{ name }?")
|
|
10
|
+
image_tag(instance.send(name).url(style))
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def attachment_thumb_style_for(instance)
|
|
17
|
+
styles = instance.send(name).styles.map(&:first)
|
|
18
|
+
# Check if there's a :thumb or :thumbnail style in attachment definition
|
|
19
|
+
thumb = styles.find { |s| %w(thumb thumbnail).include?(s.to_s) }
|
|
20
|
+
# Return the potentially smallest size !
|
|
21
|
+
thumb || styles.first || :original
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class NestedManyField < AttributeField::HasManyField
|
|
4
|
+
def parse_input(params)
|
|
5
|
+
if (nested_attributes = params[nested_attributes_key])
|
|
6
|
+
nested_attributes.each do |index, attributes|
|
|
7
|
+
nested_model_mappings.fields.each do |field|
|
|
8
|
+
field.parse_input(attributes)
|
|
9
|
+
end
|
|
10
|
+
params[nested_attributes_key][index] = attributes
|
|
11
|
+
end
|
|
12
|
+
else
|
|
13
|
+
super(params)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def nested_model_mappings
|
|
18
|
+
@nested_model_mappings ||= AttributeFieldMappings.new(reflection.klass)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def nested_attributes_key
|
|
22
|
+
@nested_attributes_key ||= :"#{ name }_attributes"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class NestedOneField < AttributeField::BelongsToField
|
|
4
|
+
def parse_input(params)
|
|
5
|
+
if (nested_attributes = params[nested_attributes_key])
|
|
6
|
+
nested_attributes.each do |index, attributes|
|
|
7
|
+
nested_model_mappings.fields.each do |field|
|
|
8
|
+
field.parse_input(attributes)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
params[nested_attributes_key][index] = attributes
|
|
12
|
+
end
|
|
13
|
+
else
|
|
14
|
+
super(params)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def nested_model_mappings
|
|
19
|
+
@nested_model_mappings ||= AttributeFieldMappings.new(reflection.klass)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def nested_attributes_key
|
|
23
|
+
@nested_attributes_key ||= :"#{ name }_attributes"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class PasswordField < AttributeField::Base
|
|
4
|
+
def initialize(model, options = {})
|
|
5
|
+
options.reverse_merge!(type: 'password', field_type: 'password')
|
|
6
|
+
super(model, options)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def value_for(instance)
|
|
10
|
+
nil
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def parse_input(params)
|
|
14
|
+
params[name] = params[name].presence
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class Redactor < AttributeField::Base
|
|
4
|
+
include ActionView::Helpers::SanitizeHelper
|
|
5
|
+
include ActionView::Helpers::TextHelper
|
|
6
|
+
|
|
7
|
+
def initialize(model, options = {})
|
|
8
|
+
options.reverse_merge!(type: 'text', field_type: 'redactor')
|
|
9
|
+
super(model, options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def value_for(instance)
|
|
13
|
+
(value = instance.send(name)) && truncate(
|
|
14
|
+
sanitize(value, tags: []),
|
|
15
|
+
length: 50
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module AttributeField
|
|
3
|
+
class RelationField < AttributeField::Base
|
|
4
|
+
private
|
|
5
|
+
|
|
6
|
+
def reflection
|
|
7
|
+
@reflection ||= model.reflect_on_association(name)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def foreign_key
|
|
11
|
+
@foreign_key ||= reflection && case reflection.macro
|
|
12
|
+
when :belongs_to then reflection.foreign_key
|
|
13
|
+
when :has_one then :"#{ reflection.name }_id"
|
|
14
|
+
when :has_many then :"#{ reflection.name.to_s.singularize }_ids"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def resource_name(resource)
|
|
19
|
+
Para.config.resource_name_methods.each do |method|
|
|
20
|
+
return resource.send(method) if resource.respond_to?(method)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
model_name = resource.class.model_name.human
|
|
24
|
+
"#{ model_name } - #{ resource.id }"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Takes an array of ids and a block. Check for each id if model exists
|
|
28
|
+
# and create one if not. E.g: [12, "foo"] will try to create a model with
|
|
29
|
+
# 'foo'
|
|
30
|
+
def on_the_fly_creation ids, &block
|
|
31
|
+
Array.wrap(ids).each do |id|
|
|
32
|
+
if !reflection.klass.exists?(id: id)
|
|
33
|
+
resource = reflection.klass.new
|
|
34
|
+
|
|
35
|
+
Para.config.resource_name_methods.each do |method_name|
|
|
36
|
+
setter_name = :"#{ method_name }="
|
|
37
|
+
|
|
38
|
+
if resource.respond_to?(setter_name)
|
|
39
|
+
resource.send(setter_name, id)
|
|
40
|
+
|
|
41
|
+
if resource.save
|
|
42
|
+
block.call(resource, id)
|
|
43
|
+
break
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'para/attribute_field/base'
|
|
2
|
+
require 'para/attribute_field/boolean'
|
|
3
|
+
require 'para/attribute_field/datetime'
|
|
4
|
+
require 'para/attribute_field/password'
|
|
5
|
+
require 'para/attribute_field/file'
|
|
6
|
+
require 'para/attribute_field/image'
|
|
7
|
+
require 'para/attribute_field/relation'
|
|
8
|
+
require 'para/attribute_field/belongs_to'
|
|
9
|
+
require 'para/attribute_field/has_many'
|
|
10
|
+
require 'para/attribute_field/nested_one'
|
|
11
|
+
require 'para/attribute_field/nested_many'
|
|
12
|
+
require 'para/attribute_field/redactor'
|
|
13
|
+
require 'para/attribute_field/translation'
|
|
14
|
+
|
|
15
|
+
module Para
|
|
16
|
+
class AttributeFieldMappings
|
|
17
|
+
UNEDITABLE_ATTRIBUTES = %w(id component_id created_at updated_at type)
|
|
18
|
+
|
|
19
|
+
attr_reader :model, :fields_hash, :whitelist_attributes
|
|
20
|
+
|
|
21
|
+
def initialize(model, whitelist_attributes: nil)
|
|
22
|
+
@model = model
|
|
23
|
+
@whitelist_attributes = whitelist_attributes
|
|
24
|
+
|
|
25
|
+
process_fields!
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def fields
|
|
29
|
+
fields_hash.values
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def field_for(field_name, type = nil)
|
|
33
|
+
fields_hash[field_name] ||= if model.new.respond_to?(field_name)
|
|
34
|
+
build_field_for(field_name, type)
|
|
35
|
+
else
|
|
36
|
+
raise NoMethodError.new(
|
|
37
|
+
"No attribute or method correspond to ##{ field_name } " +
|
|
38
|
+
"in the model #{ model.name }. No field could be created."
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def whitelisted?(attribute_name)
|
|
46
|
+
!whitelist_attributes || whitelist_attributes.include?(attribute_name)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def process_fields!
|
|
50
|
+
@fields_hash = model.columns.each_with_object({}) do |column, fields|
|
|
51
|
+
next unless whitelisted?(column.name)
|
|
52
|
+
|
|
53
|
+
# Reject uneditable attributes
|
|
54
|
+
unless UNEDITABLE_ATTRIBUTES.include?(column.name)
|
|
55
|
+
field_class = field_class_for(column.type)
|
|
56
|
+
|
|
57
|
+
fields[column.name] = field_class.new(
|
|
58
|
+
model, name: column.name, type: column.type
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
end.with_indifferent_access
|
|
62
|
+
|
|
63
|
+
Para::ModelFieldParsers.parse!(model, fields_hash)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def build_field_for(attribute_name, type)
|
|
67
|
+
field_class = field_class_for(type)
|
|
68
|
+
|
|
69
|
+
field_class.new(
|
|
70
|
+
model, name: attribute_name, type: type
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def field_class_for(type)
|
|
75
|
+
case type
|
|
76
|
+
when :boolean then AttributeField::BooleanField
|
|
77
|
+
when :date, :datetime then AttributeField::DatetimeField
|
|
78
|
+
else AttributeField::Base
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module Cloneable
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
class_attribute :cloneable_associations
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def acts_as_cloneable(*args)
|
|
11
|
+
@cloneable = true
|
|
12
|
+
self.cloneable_associations = args
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def cloneable?
|
|
16
|
+
@cloneable ||= false
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Para
|
|
2
|
+
module Component
|
|
3
|
+
module Exportable
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
configurable_on :export
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def exportable?
|
|
11
|
+
@exportable ||= exports.length > 0
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# TODO : Move :configuration column store to JSON instead of HStore
|
|
15
|
+
# which handles more data types and will help us avoid eval here
|
|
16
|
+
def exports
|
|
17
|
+
@exports ||= if export.present?
|
|
18
|
+
eval(export).map(&:with_indifferent_access)
|
|
19
|
+
else
|
|
20
|
+
[]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|