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
@@ -5,9 +5,19 @@ module Para
5
5
  input_html_options[:class] << "nested-one"
6
6
 
7
7
  parent_model = object.class
8
- model = parent_model.reflect_on_association(attribute_name).klass
9
- resource = object.send(:"#{ attribute_name }") ||
10
- object.send(:"#{ attribute_name }=", model.new)
8
+ association = object.association(attribute_name)
9
+ relation = parent_model.reflect_on_association(attribute_name)
10
+ model = relation.klass
11
+
12
+ unless (resource = object.send(:"#{ attribute_name }"))
13
+ # Build association without trying to save the new record
14
+ resource = case association
15
+ when ActiveRecord::Associations::HasOneAssociation
16
+ association.replace(model.new, false)
17
+ else
18
+ association.replace(model.new)
19
+ end
20
+ end
11
21
 
12
22
  template.render(
13
23
  partial: 'para/inputs/nested_one',
@@ -11,9 +11,7 @@ module Para
11
11
  @orderable = model.orderable?
12
12
  end
13
13
 
14
- if !options.key?(:actions) || options.delete(:actions)
15
- @actions = true
16
- end
14
+ @actions = options.fetch(:actions, true)
17
15
 
18
16
  merge_class!(options, 'table')
19
17
  merge_class!(options, 'para-component-relation-table')
@@ -72,10 +70,23 @@ module Para
72
70
  cells.join("\n").html_safe
73
71
  end
74
72
 
75
- def header_for(field_name, sort: field_name)
76
- label = model.human_attribute_name(field_name)
73
+ def header_for(field_name = nil, options = {}, &block)
74
+ if Hash === field_name
75
+ options = field_name
76
+ field_name = nil
77
+ end
78
+
79
+ sort = options.delete(:sort) || field_name
77
80
 
78
- content_tag(:th) do
81
+ label = if Symbol === field_name
82
+ model.human_attribute_name(field_name)
83
+ elsif block
84
+ capture { block.call }
85
+ else
86
+ field_name
87
+ end
88
+
89
+ content_tag(:th, options) do
79
90
  if sort != field_name
80
91
  view.sort_link(search, *sort, label, hide_indicator: true)
81
92
  elsif searchable?(field_name)
@@ -94,10 +105,12 @@ module Para
94
105
  # - a single value : The value to display in the cell directly
95
106
  # which will be processed to be shorter than 100 chars
96
107
  #
97
- def data_for(*args)
108
+ def data_for(*args, &block)
98
109
  value = if args.length >= 2
99
110
  resource, field_name, type = args
100
111
  view.field_value_for(resource, field_name, type).to_s
112
+ elsif block
113
+ capture { block.call }
101
114
  else
102
115
  view.excerpt_value_for(args.first)
103
116
  end
@@ -119,9 +132,11 @@ module Para
119
132
  def actions_cell(resource)
120
133
  content_tag(:td) do
121
134
  content_tag(:div, class: 'pull-right btn-group') do
122
- edit_button(resource) +
123
- clone_button(resource) +
124
- delete_button(resource)
135
+ edit = edit_button(resource) if action?(:edit)
136
+ clone = clone_button(resource) if action?(:clone)
137
+ delete = delete_button(resource) if action?(:delete)
138
+
139
+ edit + clone + delete
125
140
  end
126
141
  end
127
142
  end
@@ -129,37 +144,42 @@ module Para
129
144
  def clone_button(resource)
130
145
  return unless resource.class.cloneable?
131
146
 
132
- view.link_to(
133
- component.relation_path(
134
- resource, action: :clone, return_to: view.request.fullpath
135
- ),
147
+ path = component.relation_path(
148
+ resource, action: :clone, return_to: view.request.fullpath
149
+ )
150
+
151
+ options = {
136
152
  method: :post,
137
153
  class: 'btn btn-info'
138
- ) do
154
+ }
155
+
156
+ view.link_to(path, options) do
139
157
  content_tag(:i, '', class: 'fa fa-refresh')
140
158
  end
141
159
  end
142
160
 
143
161
  def edit_button(resource)
144
- view.link_to(
145
- component.relation_path(
146
- resource, action: :edit, return_to: view.request.fullpath
147
- ),
148
- class: 'btn btn-primary'
149
- ) do
162
+ path = component.relation_path(
163
+ resource, action: :edit, return_to: view.request.fullpath
164
+ )
165
+
166
+ view.link_to(path, class: 'btn btn-primary') do
150
167
  content_tag(:i, '', class: 'fa fa-pencil')
151
168
  end
152
169
  end
153
170
 
154
171
  def delete_button(resource)
155
- view.link_to(
156
- component.relation_path(resource),
172
+ path = component.relation_path(resource)
173
+
174
+ options = {
157
175
  method: :delete,
158
176
  data: {
159
177
  confirm: I18n.t('para.list.delete_confirmation')
160
178
  },
161
179
  class: 'btn btn-danger'
162
- ) do
180
+ }
181
+
182
+ view.link_to(path, options) do
163
183
  content_tag(:i, '', class: 'fa fa-trash')
164
184
  end
165
185
  end
@@ -173,6 +193,10 @@ module Para
173
193
  def searchable?(field_name)
174
194
  model.columns_hash.keys.include?(field_name.to_s)
175
195
  end
196
+
197
+ def action?(type)
198
+ actions == true || actions.include?(type)
199
+ end
176
200
  end
177
201
  end
178
202
  end
@@ -0,0 +1,36 @@
1
+ module Para
2
+ module Markup
3
+ class ResourceTree < Para::Markup::Component
4
+ def tree_list(resources, options = {}, &block)
5
+ options[:class] ||= ''
6
+ options[:class] << ' tree-list'
7
+
8
+ content_tag(:ul, options) do
9
+ resources.each do |resource|
10
+ block.call(resource)
11
+ end
12
+ end
13
+ end
14
+
15
+ def tree_node(resource: nil, label: nil, children: [], **options, &block)
16
+ options[:class] ||= ''
17
+ options[:class] << ' node'
18
+
19
+ content_tag(:li, options) do
20
+ node_row(resource, label) +
21
+ node_children(children, &block)
22
+ end
23
+ end
24
+
25
+ def node_row(resource, label)
26
+ content_tag(:div, class: 'node-row') do
27
+ label
28
+ end
29
+ end
30
+
31
+ def node_children(children, &block)
32
+ tree_list(children, &block) if children.length > 0
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,23 @@
1
+ module Para
2
+ module ModelFieldParsers
3
+ class Store < Para::ModelFieldParsers::Base
4
+ register :json, self
5
+
6
+ def parse!
7
+ model.stored_attributes.each do |store_key, field_names|
8
+ fields_hash.delete(store_key)
9
+
10
+ field_names.each do |field_name|
11
+ fields_hash[field_name] = AttributeField::Base.new(
12
+ model, name: field_name, type: :string
13
+ )
14
+ end
15
+ end
16
+ end
17
+
18
+ def applicable?
19
+ !model.stored_attributes.empty?
20
+ end
21
+ end
22
+ end
23
+ end
@@ -20,3 +20,4 @@ require 'para/model_field_parsers/orderable'
20
20
  require 'para/model_field_parsers/relations'
21
21
  require 'para/model_field_parsers/redactor'
22
22
  require 'para/model_field_parsers/globalize'
23
+ require 'para/model_field_parsers/store'
@@ -4,11 +4,14 @@ module Para
4
4
 
5
5
  included do
6
6
  scope :ordered, -> { order('position ASC') }
7
- before_create :orederable_assign_position
7
+ before_create :orderable_assign_position
8
8
  end
9
9
 
10
- def orederable_assign_position
11
- last_resource = self.class.order('position DESC')
10
+ def orderable_assign_position
11
+ return if attribute_present?(:position)
12
+
13
+ last_resource = self.class.unscoped
14
+ .order('position DESC')
12
15
  .where.not(position: nil)
13
16
  .select(:position)
14
17
  .first
@@ -0,0 +1,25 @@
1
+ module Para
2
+ module Plugins
3
+ class Routes
4
+ attr_reader :router
5
+
6
+ def initialize(router)
7
+ @router = router
8
+ end
9
+
10
+ def plugin(identifier, &block)
11
+ # Store router reference in closure to allow accessing it from
12
+ # inside the below block
13
+ router = self.router
14
+
15
+ router.instance_eval do
16
+ namespace :admin do
17
+ scope module: :acl do
18
+ router.instance_eval(&block)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module Para
2
+ module Plugins
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Routes
6
+
7
+ def self.module_name_for(identifier)
8
+ ['Para', identifier.to_s.camelize].join('::')
9
+ end
10
+ end
11
+ end
data/lib/para/routes.rb CHANGED
@@ -7,37 +7,14 @@ module Para
7
7
  end
8
8
 
9
9
  def draw(mount_location = '/', &block)
10
- routes = self
11
-
12
10
  router.instance_eval do
13
11
  scope mount_location do
14
12
  scope module: :para do
15
13
  namespace :admin do
16
14
  get '/' => 'main#index'
17
15
 
18
- component :crud do
19
- scope ':model' do
20
- resources :crud_resources, path: '/' do
21
- collection do
22
- patch :order
23
- patch :tree
24
- get :export
25
- end
26
-
27
- member do
28
- post :clone
29
- end
30
- end
31
- end
32
- end
33
-
34
- component :singleton_resource, path: 'resource-form' do
35
- resource :singleton_resource, path: 'resource', only: [:create, :update]
36
- end
37
-
38
- component :settings do
39
- resource :settings_form, controller: 'settings_form', only: [:update]
40
- end
16
+ crud_component :crud, scope: ':model'
17
+ singleton_resource_component :singleton, scope: ':model'
41
18
  end
42
19
  end
43
20
 
data/lib/para/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Para
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/para.rb CHANGED
@@ -3,7 +3,6 @@ require 'paperclip'
3
3
  require 'simple_form'
4
4
  require 'simple_form_extension'
5
5
  require 'active_decorator'
6
- require 'friendly_id'
7
6
  require 'haml-rails'
8
7
  require 'truncate_html'
9
8
  require 'cocoon'
@@ -13,7 +12,6 @@ require 'request_store'
13
12
  require 'sass-rails'
14
13
  require 'selectize-rails'
15
14
  require 'bootstrap-sass'
16
- require 'compass-rails'
17
15
  require 'font-awesome-rails'
18
16
 
19
17
  require 'rails/routing_mapper'
@@ -23,6 +21,7 @@ require 'para/ext'
23
21
  require 'para/errors'
24
22
  require 'para/config'
25
23
  require 'para/model_field_parsers'
24
+ require 'para/attribute_field'
26
25
  require 'para/attribute_field_mappings'
27
26
  require 'para/inputs'
28
27
  require 'para/cloneable'
@@ -32,6 +31,7 @@ require 'para/markup'
32
31
  require 'para/engine'
33
32
  require 'para/components_configuration'
34
33
  require 'para/exporter'
34
+ require 'para/importer'
35
35
 
36
36
  module Para
37
37
  extend ActiveSupport::Autoload
@@ -41,6 +41,8 @@ module Para
41
41
  autoload :Component
42
42
  autoload :Generators
43
43
  autoload :Routes
44
+ autoload :Plugins
45
+ autoload :Breadcrumbs
44
46
  end
45
47
 
46
48
  def self.config(&block)
@@ -3,6 +3,10 @@ module ActionDispatch
3
3
  class Mapper
4
4
  def para_at(mount_location, &block)
5
5
  Para::Routes.new(self).draw(mount_location, &block)
6
+
7
+ Para::Config.plugins.each do |plugin|
8
+ draw_plugin_routes(plugin)
9
+ end
6
10
  end
7
11
 
8
12
  def para_plugin(plugin_name)
@@ -11,16 +15,71 @@ module ActionDispatch
11
15
  end
12
16
 
13
17
  def component(component_name, options = {}, &block)
18
+ as, controller, endpoint = component_informations_from(
19
+ component_name, options
20
+ )
21
+
22
+ get endpoint => "#{ controller }#show", as: as
23
+ scope(endpoint, as: component_name, &block) if block
24
+ end
25
+
26
+ def crud_component(component_name, options = {}, &block)
27
+ as, controller, endpoint = component_informations_from(
28
+ component_name, options
29
+ )
30
+
31
+ controller = [component_name.to_s.singularize, 'resources'].join('_')
32
+
33
+ scope endpoint, as: as do
34
+ resources :resources, controller: controller do
35
+ scope options[:scope] do
36
+ collection do
37
+ patch :order
38
+ patch :tree
39
+ get :export
40
+ post :import
41
+ end
42
+
43
+ member do
44
+ post :clone
45
+ end
46
+
47
+ instance_eval(&block) if block
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ def singleton_resource_component(component_name, options = {}, &block)
54
+ as, _, endpoint = component_informations_from(
55
+ component_name, options
56
+ )
57
+
58
+ controller = [component_name, 'resources'].join('_')
59
+
60
+ scope endpoint, as: as do
61
+ resource :resource, controller: controller, only: [:show, :create, :update]
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def draw_plugin_routes(identifier)
68
+ routes = [
69
+ Para::Plugins.module_name_for(identifier),
70
+ 'Routes'
71
+ ].join('::').constantize
72
+
73
+ routes.new(self).draw
74
+ end
75
+
76
+ def component_informations_from(component_name, options)
14
77
  path = options.fetch(:path, component_name.to_s)
15
78
  as = options.fetch(:as, component_name)
16
79
  controller = options.fetch(:controller, "#{ component_name }_component")
17
-
18
80
  endpoint = "#{ path }/:component_id"
19
81
 
20
- get endpoint => "#{ controller }#show", as: as
21
- delete endpoint => "#{ controller }#destroy", as: "destroy_#{as}"
22
-
23
- scope(endpoint, as: component_name, &block) if block
82
+ [as, controller, endpoint]
24
83
  end
25
84
  end
26
85
  end