para 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
+