para 0.4.0 → 0.5.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.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/para/admin/loading-spinner.gif +0 -0
  3. data/app/assets/javascripts/para/admin/theme_actions.coffee +0 -1
  4. data/app/assets/javascripts/para/lib/turbolinks-forms.coffee +23 -0
  5. data/app/assets/javascripts/para/lib/turbolinks-loading.coffee +25 -0
  6. data/app/assets/stylesheets/para/{overrides → admin/src}/datetimepicker.sass +1 -3
  7. data/app/assets/stylesheets/para/{overrides → admin/src}/fuelux.sass +5 -8
  8. data/app/assets/stylesheets/para/{overrides → admin/src}/jasny.bootstrap.sass +1 -2
  9. data/app/assets/stylesheets/para/admin/src/page-loading.sass +24 -0
  10. data/app/assets/stylesheets/para/{overrides → admin/src}/redactor.sass +5 -7
  11. data/app/assets/stylesheets/para/{overrides → admin/src}/selectize.sass +4 -6
  12. data/app/assets/stylesheets/para/{overrides → admin/src}/slider.sass +0 -1
  13. data/app/assets/stylesheets/para/admin/theme/_base.sass +5 -3
  14. data/app/assets/stylesheets/para/admin/theme/_breadcrumb.sass +54 -16
  15. data/app/assets/stylesheets/para/admin/theme/_buttons.sass +0 -1
  16. data/app/assets/stylesheets/para/admin/theme/_checkable.sass +5 -6
  17. data/app/assets/stylesheets/para/admin/theme/_commonds.sass +10 -16
  18. data/app/assets/stylesheets/para/admin/theme/_dropdown.sass +1 -4
  19. data/app/assets/stylesheets/para/admin/theme/_form.sass +11 -3
  20. data/app/assets/stylesheets/para/admin/theme/_list.sass +5 -6
  21. data/app/assets/stylesheets/para/admin/theme/_navigation.sass +105 -109
  22. data/app/assets/stylesheets/para/admin/theme/_navtabs.sass +35 -15
  23. data/app/assets/stylesheets/para/admin/theme/_orderable.sass +2 -5
  24. data/app/assets/stylesheets/para/admin/theme/_panel.sass +20 -50
  25. data/app/assets/stylesheets/para/admin/theme/_sorting.sass +5 -7
  26. data/app/assets/stylesheets/para/admin/theme/_tree.sass +2 -4
  27. data/app/assets/stylesheets/para/admin.sass +1 -0
  28. data/app/assets/stylesheets/para/lib/_variables.scss +12 -12
  29. data/app/assets/stylesheets/para/overrides/responsive.sass +1 -2
  30. data/app/assets/stylesheets/para/overrides/theme.sass +10 -17
  31. data/app/controllers/para/admin/base_controller.rb +16 -5
  32. data/app/controllers/para/admin/crud_resources_controller.rb +43 -2
  33. data/app/controllers/para/admin/main_controller.rb +1 -1
  34. data/app/controllers/para/admin/resources_controller.rb +1 -1
  35. data/app/controllers/para/admin/settings_component_controller.rb +1 -3
  36. data/app/controllers/para/application_controller.rb +3 -3
  37. data/app/decorators/para/component/crud_decorator.rb +6 -2
  38. data/app/decorators/para/component/singleton_resource_decorator.rb +5 -1
  39. data/app/helpers/para/admin/base_helper.rb +9 -4
  40. data/app/helpers/para/admin/components_helper.rb +7 -0
  41. data/app/helpers/para/form_helper.rb +10 -2
  42. data/app/helpers/para/tag_helper.rb +12 -0
  43. data/app/models/para/ability.rb +12 -0
  44. data/app/models/para/component/base.rb +15 -7
  45. data/app/models/para/component/crud.rb +3 -1
  46. data/app/models/para/component/singleton_resource.rb +1 -1
  47. data/app/views/para/admin/crud_resources/index.html.haml +4 -0
  48. data/app/views/para/admin/dashboard.html.haml +10 -8
  49. data/app/views/para/admin/resources/_actions.html.haml +7 -0
  50. data/app/views/para/admin/resources/_add_button.html.haml +1 -0
  51. data/app/views/para/admin/resources/_filters.html.haml +1 -1
  52. data/app/views/para/admin/resources/_imports_menu.html.haml +22 -0
  53. data/app/views/para/admin/resources/_list.html.haml +8 -5
  54. data/app/views/para/admin/resources/_subclassable_add_button.html.haml +10 -0
  55. data/app/views/para/admin/resources/_tree.html.haml +1 -1
  56. data/app/views/para/admin/resources/_tree_item.html.haml +1 -1
  57. data/app/views/para/admin/resources/new.html.haml +1 -1
  58. data/app/views/para/admin/shared/_breadcrumbs.html.haml +8 -0
  59. data/app/views/para/admin/shared/_header.html.haml +26 -24
  60. data/app/views/para/admin/shared/_navigation.html.haml +5 -3
  61. data/app/views/para/form/_tabs.html.haml +13 -0
  62. data/app/views/para/inputs/_nested_many.html.haml +2 -2
  63. data/app/views/para/inputs/_nested_many_container.html.haml +1 -3
  64. data/config/locales/en.yml +10 -0
  65. data/config/locales/fr.yml +30 -15
  66. data/db/migrate/20160304113055_add_json_equality_operator_patch_to_postgres.rb +38 -0
  67. data/lib/generators/para/component/component_generator.rb +9 -29
  68. data/lib/generators/para/component/crud/crud_generator.rb +41 -0
  69. data/lib/generators/para/component/templates/component.rb +1 -1
  70. data/lib/generators/para/component/templates/decorator.rb +3 -0
  71. data/lib/generators/para/component/templates/resources_controller.rb +4 -0
  72. data/lib/generators/para/exporter/templates/base_exporter.rb +2 -0
  73. data/lib/generators/para/exporter/templates/csv_exporter.rb +2 -0
  74. data/lib/generators/para/filters/filters_generator.rb +16 -0
  75. data/lib/generators/para/filters/templates/_filters.html.haml +9 -0
  76. data/lib/generators/para/importer/importer_generator.rb +20 -0
  77. data/lib/generators/para/importer/templates/base_importer.rb +5 -0
  78. data/lib/generators/para/install/install_generator.rb +6 -25
  79. data/lib/generators/para/nested_fields/nested_fields_generator.rb +9 -0
  80. data/lib/para/attribute_field/base.rb +28 -0
  81. data/lib/para/attribute_field/belongs_to.rb +2 -0
  82. data/lib/para/attribute_field/boolean.rb +2 -0
  83. data/lib/para/attribute_field/datetime.rb +2 -0
  84. data/lib/para/attribute_field/enum.rb +22 -0
  85. data/lib/para/attribute_field/file.rb +2 -0
  86. data/lib/para/attribute_field/has_many.rb +2 -0
  87. data/lib/para/attribute_field/image.rb +2 -0
  88. data/lib/para/attribute_field/nested_many.rb +3 -0
  89. data/lib/para/attribute_field/nested_one.rb +6 -6
  90. data/lib/para/attribute_field/password.rb +2 -0
  91. data/lib/para/attribute_field/redactor.rb +2 -0
  92. data/lib/para/attribute_field/translation.rb +2 -0
  93. data/lib/para/attribute_field.rb +19 -0
  94. data/lib/para/attribute_field_mappings.rb +15 -29
  95. data/lib/para/breadcrumbs/breadcrumb.rb +43 -0
  96. data/lib/para/breadcrumbs/controller.rb +39 -0
  97. data/lib/para/breadcrumbs/manager.rb +19 -0
  98. data/lib/para/breadcrumbs.rb +9 -0
  99. data/lib/para/component/importable.rb +25 -0
  100. data/lib/para/component/subclassable.rb +27 -0
  101. data/lib/para/component.rb +5 -3
  102. data/lib/para/components_configuration.rb +18 -2
  103. data/lib/para/config.rb +25 -1
  104. data/lib/para/exporter/base.rb +13 -0
  105. data/lib/para/exporter/csv.rb +1 -1
  106. data/lib/para/form_builder/tabs.rb +56 -0
  107. data/lib/para/form_builder.rb +2 -0
  108. data/lib/para/generators/component_helpers.rb +57 -0
  109. data/lib/para/generators.rb +1 -0
  110. data/lib/para/importer/base.rb +23 -0
  111. data/lib/para/importer.rb +10 -0
  112. data/lib/para/inputs/nested_one_input.rb +13 -3
  113. data/lib/para/markup/resources_table.rb +48 -24
  114. data/lib/para/markup/resources_tree.rb +36 -0
  115. data/lib/para/model_field_parsers/store.rb +23 -0
  116. data/lib/para/model_field_parsers.rb +1 -0
  117. data/lib/para/orderable.rb +6 -3
  118. data/lib/para/plugins/routes.rb +25 -0
  119. data/lib/para/plugins.rb +11 -0
  120. data/lib/para/routes.rb +2 -25
  121. data/lib/para/version.rb +1 -1
  122. data/lib/para.rb +4 -2
  123. data/lib/rails/routing_mapper.rb +64 -5
  124. metadata +50 -64
  125. data/app/controllers/para/admin/crud_component_controller.rb +0 -17
  126. data/app/controllers/para/admin/singleton_resource_component_controller.rb +0 -14
  127. data/app/views/para/admin/component_sections/_form.html.haml +0 -10
  128. data/app/views/para/admin/component_sections/edit.html.haml +0 -5
  129. data/app/views/para/admin/component_sections/new.html.haml +0 -5
  130. data/app/views/para/admin/components/_form.html.haml +0 -15
  131. data/app/views/para/admin/components/new.html.haml +0 -4
  132. data/app/views/para/admin/shared/_breadcrumb.html.haml +0 -3
  133. /data/app/views/para/admin/{singleton_resource_component → singleton_resources}/show.html.haml +0 -0
@@ -0,0 +1,22 @@
1
+ module Para
2
+ module AttributeField
3
+ class EnumField < AttributeField::Base
4
+ register :enum, self
5
+
6
+ def value_for(instance)
7
+ if (raw_value = instance.send(name)) &&
8
+ path = enum_path_for(instance, raw_value)
9
+ translation = I18n.t("activerecord.#{ path }", default: false)
10
+
11
+ translation || raw_value
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def enum_path_for(instance, key)
18
+ ['enums', instance.class.model_name.i18n_key, name, key].join('.')
19
+ end
20
+ end
21
+ end
22
+ end
@@ -3,6 +3,8 @@ module Para
3
3
  class FileField < AttributeField::Base
4
4
  include ActionView::Helpers::UrlHelper
5
5
 
6
+ register :file, self
7
+
6
8
  field_option :wrapper, :wrapper_name
7
9
 
8
10
  def value_for(instance)
@@ -1,6 +1,8 @@
1
1
  module Para
2
2
  module AttributeField
3
3
  class HasManyField < RelationField
4
+ register :has_many, self
5
+
4
6
  def field_name
5
7
  reflection.name
6
8
  end
@@ -3,6 +3,8 @@ module Para
3
3
  class ImageField < AttributeField::Base
4
4
  include ActionView::Helpers::AssetTagHelper
5
5
 
6
+ register :image, self
7
+
6
8
  def value_for(instance)
7
9
  style = attachment_thumb_style_for(instance)
8
10
 
@@ -1,12 +1,15 @@
1
1
  module Para
2
2
  module AttributeField
3
3
  class NestedManyField < AttributeField::HasManyField
4
+ register :nested_many, self
5
+
4
6
  def parse_input(params)
5
7
  if (nested_attributes = params[nested_attributes_key])
6
8
  nested_attributes.each do |index, attributes|
7
9
  nested_model_mappings.fields.each do |field|
8
10
  field.parse_input(attributes)
9
11
  end
12
+
10
13
  params[nested_attributes_key][index] = attributes
11
14
  end
12
15
  else
@@ -1,15 +1,15 @@
1
1
  module Para
2
2
  module AttributeField
3
3
  class NestedOneField < AttributeField::BelongsToField
4
+ register :nested_one, self
5
+
4
6
  def parse_input(params)
5
7
  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
8
+ nested_model_mappings.fields.each do |field|
9
+ field.parse_input(nested_attributes)
12
10
  end
11
+
12
+ params[nested_attributes_key] = nested_attributes
13
13
  else
14
14
  super(params)
15
15
  end
@@ -1,6 +1,8 @@
1
1
  module Para
2
2
  module AttributeField
3
3
  class PasswordField < AttributeField::Base
4
+ register :password, self
5
+
4
6
  def initialize(model, options = {})
5
7
  options.reverse_merge!(type: 'password', field_type: 'password')
6
8
  super(model, options)
@@ -4,6 +4,8 @@ module Para
4
4
  include ActionView::Helpers::SanitizeHelper
5
5
  include ActionView::Helpers::TextHelper
6
6
 
7
+ register :redactor, self
8
+
7
9
  def initialize(model, options = {})
8
10
  options.reverse_merge!(type: 'text', field_type: 'redactor')
9
11
  super(model, options)
@@ -1,6 +1,8 @@
1
1
  module Para
2
2
  module AttributeField
3
3
  class Translation < AttributeField::Base
4
+ register :translation, self
5
+
4
6
  def attribute_column_path
5
7
  [:translations, name]
6
8
  end
@@ -0,0 +1,19 @@
1
+ module Para
2
+ module AttributeField
3
+ end
4
+ end
5
+
6
+ require 'para/attribute_field/base'
7
+ require 'para/attribute_field/boolean'
8
+ require 'para/attribute_field/datetime'
9
+ require 'para/attribute_field/password'
10
+ require 'para/attribute_field/file'
11
+ require 'para/attribute_field/image'
12
+ require 'para/attribute_field/enum'
13
+ require 'para/attribute_field/relation'
14
+ require 'para/attribute_field/belongs_to'
15
+ require 'para/attribute_field/has_many'
16
+ require 'para/attribute_field/nested_one'
17
+ require 'para/attribute_field/nested_many'
18
+ require 'para/attribute_field/redactor'
19
+ require 'para/attribute_field/translation'
@@ -1,17 +1,3 @@
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
1
  module Para
16
2
  class AttributeFieldMappings
17
3
  UNEDITABLE_ATTRIBUTES = %w(id component_id created_at updated_at type)
@@ -30,13 +16,19 @@ module Para
30
16
  end
31
17
 
32
18
  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)
19
+ existing_field = fields_hash[field_name]
20
+
21
+ if !existing_field || (type && !existing_field.type?(type))
22
+ fields_hash[field_name] = if model.new.respond_to?(field_name)
23
+ build_field_for(field_name, type)
24
+ else
25
+ raise NoMethodError.new(
26
+ "No attribute or method correspond to ##{ field_name } " +
27
+ "in the model #{ model.name }. No field could be created."
28
+ )
29
+ end
35
30
  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
- )
31
+ existing_field
40
32
  end
41
33
  end
42
34
 
@@ -65,18 +57,12 @@ module Para
65
57
 
66
58
  def build_field_for(attribute_name, type)
67
59
  field_class = field_class_for(type)
68
-
69
- field_class.new(
70
- model, name: attribute_name, type: type
71
- )
60
+ field_class.new(model, name: attribute_name, type: type)
72
61
  end
73
62
 
74
63
  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
64
+ attribute_class = type && AttributeField::Base.field_types[type.to_sym]
65
+ attribute_class || AttributeField::Base
80
66
  end
81
67
  end
82
68
  end
@@ -0,0 +1,43 @@
1
+ module Para
2
+ module Breadcrumbs
3
+ class Breadcrumb
4
+ attr_reader :identifier, :_path, :options
5
+
6
+ def initialize(identifier, path, *options)
7
+ @identifier = identifier
8
+ @_path = path
9
+ @options = options
10
+ end
11
+
12
+ def title
13
+ @title ||= if Symbol === identifier
14
+ I18n.t("admin.breadcrumbs.#{ identifier }")
15
+ else
16
+ identifier
17
+ end
18
+ end
19
+
20
+ # Allow lazy evaluation of routes to define breadcrumbs before being
21
+ # able to access request or routes
22
+ def path
23
+ @path ||= if Symbol === _path
24
+ find_route_for(_path, *options)
25
+ elsif _path
26
+ _path
27
+ else
28
+ '#'
29
+ end
30
+ end
31
+
32
+ def active?(request)
33
+ path == request.path || !_path
34
+ end
35
+
36
+ private
37
+
38
+ def find_route_for(path, *options)
39
+ Rails.application.routes.url_helpers.send(:"#{ path }_path", *options)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,39 @@
1
+ module Para
2
+ module Breadcrumbs
3
+ module Controller
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :_class_level_breadcrumbs
8
+
9
+ helper_method :add_breadcrumb, :breadcrumbs
10
+ helper ViewHelper
11
+ end
12
+
13
+ def add_breadcrumb(*args)
14
+ breadcrumbs.add(*args)
15
+ end
16
+
17
+ def breadcrumbs
18
+ Para.store[:breadcrumbs] ||= begin
19
+ manager = Breadcrumbs::Manager.new
20
+ _class_level_breadcrumbs.each { |args| manager.add(*args) }
21
+ manager
22
+ end
23
+ end
24
+
25
+ module ClassMethods
26
+ def add_breadcrumb(*args)
27
+ self._class_level_breadcrumbs ||= []
28
+ self._class_level_breadcrumbs += [args]
29
+ end
30
+ end
31
+
32
+ module ViewHelper
33
+ def render_breadcrumbs
34
+ render partial: 'para/admin/shared/breadcrumbs'
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ module Para
2
+ module Breadcrumbs
3
+ class Manager
4
+ include Enumerable
5
+
6
+ def add(identifier, path = nil, *args)
7
+ breadcrumbs << Breadcrumb.new(identifier, path, *args)
8
+ end
9
+
10
+ def breadcrumbs
11
+ @breadcrumbs ||= []
12
+ end
13
+
14
+ def each(&block)
15
+ breadcrumbs.each(&block)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module Para
2
+ module Breadcrumbs
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Manager
6
+ autoload :Breadcrumb
7
+ autoload :Controller
8
+ end
9
+ end
@@ -0,0 +1,25 @@
1
+ module Para
2
+ module Component
3
+ module Importable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ configurable_on :importers
8
+ end
9
+
10
+ def importable?
11
+ @importable ||= imports.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 imports
17
+ @imports ||= if importers.present?
18
+ eval(importers)
19
+ else
20
+ []
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ module Para
2
+ module Component
3
+ module Subclassable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ configurable_on :subclasses
8
+ end
9
+
10
+ def subclassable?
11
+ @subclassable ||= subclass_names.length > 0
12
+ end
13
+
14
+ def subclassable_with?(class_name)
15
+ subclassable? && class_name.in?(subclass_names)
16
+ end
17
+
18
+ def subclass_names
19
+ @subclass_names ||= if subclasses.present?
20
+ eval(subclasses)
21
+ else
22
+ []
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -4,9 +4,9 @@ module Para
4
4
  @registered_components ||= {}
5
5
  end
6
6
 
7
- def self.registered_component?(model_name)
8
- registered_components.any? do |name, component|
9
- component.name == model_name
7
+ def self.registered_component?(identifier)
8
+ registered_components.any? do |component_identifier, _|
9
+ component_identifier == identifier
10
10
  end
11
11
  end
12
12
 
@@ -18,6 +18,8 @@ end
18
18
 
19
19
  # Require concerns
20
20
  require 'para/component/exportable'
21
+ require 'para/component/importable'
22
+ require 'para/component/subclassable'
21
23
 
22
24
  # Require models
23
25
  require 'para/component/base'
@@ -1,5 +1,8 @@
1
1
  module Para
2
2
  class ComponentsConfiguration
3
+ class UndefinedComponentTypeError < StandardError
4
+ end
5
+
3
6
  def draw(&block)
4
7
  return unless components_installed?
5
8
  eager_load_components!
@@ -85,6 +88,12 @@ module Para
85
88
  tables_exist
86
89
  end
87
90
 
91
+ # Eager loads every file ending with _component.rb that's included in a
92
+ # $LOAD_PATH directory which ends in "/components"
93
+ #
94
+ # Note : This allows not to process too many folders, but makes it harder to
95
+ # plug gems into the components system
96
+ #
88
97
  def eager_load_components!
89
98
  $LOAD_PATH.each do |path|
90
99
  next unless path.match(/\/components$/)
@@ -127,10 +136,17 @@ module Para
127
136
  class Component
128
137
  attr_accessor :identifier, :type, :options, :model
129
138
 
130
- def initialize(identifier, type, options = {})
139
+ def initialize(identifier, type_identifier, options = {})
131
140
  self.identifier = identifier.to_s
132
- self.type = Para::Component.registered_components[type]
141
+ self.type = Para::Component.registered_components[type_identifier]
133
142
  self.options = options
143
+
144
+ unless type
145
+ raise UndefinedComponentTypeError.new(
146
+ "Undefined Para component : #{ type_identifier }. " +
147
+ "Please ensure that your app or gems define this component type."
148
+ )
149
+ end
134
150
  end
135
151
 
136
152
  def refresh(attributes = {})
data/lib/para/config.rb CHANGED
@@ -16,6 +16,30 @@ module Para
16
16
  @@default_tree_max_depth = 3
17
17
 
18
18
  mattr_accessor :resource_name_methods
19
- @@resource_name_methods = [:name, :title]
19
+ @@resource_name_methods = [:admin_name, :admin_title, :name, :title]
20
+
21
+ mattr_accessor :ability_class_name
22
+ @@ability_class_name = 'Para::Ability'
23
+
24
+ mattr_accessor :plugins
25
+ @@plugins = []
26
+
27
+ # Allows accessing plugins root module to configure them through a method
28
+ # from the Para::Config class.
29
+ #
30
+ # Example :
31
+ #
32
+ # Para.config do |config|
33
+ # config.my_plugin.my_var = 'foo'
34
+ # end
35
+ #
36
+ def self.method_missing(method_name, *args, &block)
37
+ if plugins.include?(method_name)
38
+ plugin = Para::Plugins.module_name_for(method_name).constantize
39
+ block ? block.call(plugin) : plugin
40
+ else
41
+ super
42
+ end
43
+ end
20
44
  end
21
45
  end
@@ -2,11 +2,24 @@ module Para
2
2
  module Exporter
3
3
  class Base
4
4
  attr_reader :resources
5
+ class_attribute :model_name
5
6
 
6
7
  def initialize(resources)
7
8
  @resources = resources
8
9
  end
9
10
 
11
+ def model
12
+ @model ||= if (model_name = self.class.model_name)
13
+ model_name.constantize
14
+ else
15
+ raise 'You must define model to export in your exporter as following: `exports \'YourModelName\'`'
16
+ end
17
+ end
18
+
19
+ def self.exports model_name
20
+ self.model_name = model_name
21
+ end
22
+
10
23
  def disposition
11
24
  'inline'
12
25
  end
@@ -31,7 +31,7 @@ module Para
31
31
 
32
32
  def headers
33
33
  fields.map do |field|
34
- encode(User.human_attribute_name(field))
34
+ encode(model.human_attribute_name(field))
35
35
  end
36
36
  end
37
37
 
@@ -0,0 +1,56 @@
1
+ module Para
2
+ module FormBuilder
3
+ module Tabs
4
+ def tabs(&block)
5
+ tabs_manager = TabsManager.new(template, object)
6
+ block.call(tabs_manager)
7
+
8
+ template.render partial: 'para/form/tabs', locals: { tabs: tabs_manager.tabs }
9
+ end
10
+
11
+ class TabsManager
12
+ attr_reader :template, :object
13
+
14
+ def initialize(template, object)
15
+ @template = template
16
+ @object = object
17
+ end
18
+
19
+ def tab(identifier, options = {}, &block)
20
+ tabs << Tab.new(template, object, identifier, options, &block)
21
+ nil
22
+ end
23
+
24
+ def tabs
25
+ @tabs ||= []
26
+ end
27
+ end
28
+
29
+ class Tab
30
+ attr_reader :template, :object, :identifier, :icon, :content
31
+
32
+ delegate :capture, to: :template
33
+
34
+ def initialize(template, object, identifier, options, &content_block)
35
+ @template = template
36
+ @object = object
37
+ @identifier = identifier
38
+ @content = capture { content_block.call }
39
+ @icon = options[:icon]
40
+ end
41
+
42
+ def title
43
+ if Symbol === identifier
44
+ I18n.t("forms.tabs.#{ object.class.model_name.i18n_key }.#{ identifier }")
45
+ else
46
+ identifier
47
+ end
48
+ end
49
+
50
+ def dom_id
51
+ @dom_id = identifier.to_s.parameterize
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -5,6 +5,7 @@ require 'para/form_builder/field_mappings'
5
5
  require 'para/form_builder/nested_form'
6
6
  require 'para/form_builder/ordering'
7
7
  require 'para/form_builder/settings'
8
+ require 'para/form_builder/tabs'
8
9
 
9
10
  # We'll implement our own form builder later, but for now it would need to
10
11
  # patch or override Cocoon to allow creating nested fields with our custom
@@ -17,6 +18,7 @@ SimpleForm::FormBuilder.class_eval do
17
18
  include Para::FormBuilder::NestedForm
18
19
  include Para::FormBuilder::Ordering
19
20
  include Para::FormBuilder::Settings
21
+ include Para::FormBuilder::Tabs
20
22
  end
21
23
 
22
24
  SimpleForm::FormBuilder.map_type(:inet, to: SimpleForm::Inputs::StringInput)
@@ -0,0 +1,57 @@
1
+ module Para
2
+ module Generators
3
+ module ComponentHelpers
4
+ private
5
+
6
+ def add_component_to_routes(component_type, name)
7
+ route_file = File.read(Rails.root.join('config/routes.rb'))
8
+
9
+ unless route_file.match /^\s+namespace :admin do/
10
+ route "namespace :admin do\n end\n"
11
+ end
12
+
13
+ inject_into_file 'config/routes.rb', after: ' namespace :admin do' do
14
+ "\n #{ component_type } :#{ name }"
15
+ end
16
+ end
17
+
18
+ def component_name
19
+ if class_name.match(/Component/i)
20
+ class_name
21
+ else
22
+ "#{ class_name }Component"
23
+ end
24
+ end
25
+
26
+ def component_file_name
27
+ if file_name.match(/component/i)
28
+ file_name
29
+ else
30
+ "#{ file_name }_component"
31
+ end
32
+ end
33
+
34
+ def decorator_parent_name
35
+ @decorator_parent_name ||= [component_parent_name, 'Decorator'].join
36
+ end
37
+
38
+ def resources_controller_name
39
+ @resources_name ||= [
40
+ singular_name.singularize, 'resources', 'controller'
41
+ ].join('_')
42
+ end
43
+
44
+ def controller_name
45
+ @controller_name ||= resources_controller_name.camelize
46
+ end
47
+
48
+ def decorator_file_name
49
+ @decorator_file_name ||= [component_file_name, 'decorator'].join('_')
50
+ end
51
+
52
+ def decorator_name
53
+ @decorator_name ||= decorator_file_name.camelize
54
+ end
55
+ end
56
+ end
57
+ end
@@ -7,5 +7,6 @@ module Para
7
7
  autoload :FieldHelpers
8
8
  autoload :NamedBase
9
9
  autoload :NameHelpers
10
+ autoload :ComponentHelpers
10
11
  end
11
12
  end
@@ -0,0 +1,23 @@
1
+ module Para
2
+ module Importer
3
+ class Base
4
+ attr_reader :sheet
5
+
6
+ def initialize(file)
7
+ @sheet = Roo::Spreadsheet.open(file.path)
8
+ end
9
+
10
+ def run
11
+ ActiveRecord::Base.transaction do
12
+ (2..(sheet.last_row)).each do |index|
13
+ import_from_row(sheet.row(index))
14
+ end
15
+ end
16
+ end
17
+
18
+ def import_from_row(row)
19
+ raise '#import_from_row(row) must be defined'
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ module Para
2
+ module Importer
3
+ def self.model_importer_name(model_name)
4
+ [model_name.to_s.pluralize, 'importer'].join
5
+ end
6
+ end
7
+ end
8
+
9
+ require 'para/importer/base'
10
+