trestle 0.8.6 → 0.8.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +10 -9
  3. data/app/assets/javascripts/trestle/components/_dialog.js +29 -8
  4. data/app/assets/javascripts/trestle/components/_form.js +28 -7
  5. data/app/assets/javascripts/trestle/components/_sidebar.js +10 -8
  6. data/app/assets/javascripts/trestle/components/_tabs.js +2 -1
  7. data/app/assets/javascripts/trestle/components/_tooltips.js +16 -0
  8. data/app/assets/stylesheets/trestle/components/_modal.scss +4 -0
  9. data/app/assets/stylesheets/trestle/components/_navigation.scss +31 -11
  10. data/app/assets/stylesheets/trestle/components/_sidebar.scss +2 -2
  11. data/app/assets/stylesheets/trestle/components/_tags.scss +9 -0
  12. data/app/assets/stylesheets/trestle/components/_wells.scss +9 -1
  13. data/app/assets/stylesheets/trestle/core/_defaults.scss +4 -4
  14. data/app/assets/stylesheets/trestle/core/_layout.scss +8 -0
  15. data/app/assets/stylesheets/trestle/core/_typography.scss +39 -0
  16. data/app/controllers/concerns/trestle/controller/breadcrumbs.rb +21 -0
  17. data/app/controllers/concerns/trestle/controller/callbacks.rb +21 -0
  18. data/app/controllers/concerns/trestle/controller/dialog.rb +16 -0
  19. data/app/controllers/concerns/trestle/controller/helpers.rb +18 -0
  20. data/app/controllers/concerns/trestle/controller/layout.rb +16 -0
  21. data/app/controllers/concerns/trestle/controller/location.rb +15 -0
  22. data/app/controllers/trestle/application_controller.rb +6 -34
  23. data/app/helpers/trestle/debug_helper.rb +11 -0
  24. data/app/helpers/trestle/format_helper.rb +7 -3
  25. data/app/helpers/trestle/headings_helper.rb +27 -0
  26. data/app/helpers/trestle/panel_helper.rb +24 -0
  27. data/app/helpers/trestle/table_helper.rb +41 -2
  28. data/app/helpers/trestle/url_helper.rb +3 -1
  29. data/app/views/layouts/trestle/admin.html.erb +1 -1
  30. data/app/views/trestle/application/_flash.html.erb +1 -1
  31. data/app/views/trestle/shared/_sidebar.html.erb +2 -2
  32. data/app/views/trestle/table/_table.html.erb +2 -6
  33. data/lib/generators/trestle/resource/templates/admin.rb.erb +2 -2
  34. data/lib/trestle.rb +6 -4
  35. data/lib/trestle/adapters/active_record_adapter.rb +0 -4
  36. data/lib/trestle/adapters/adapter.rb +15 -10
  37. data/lib/trestle/adapters/sequel_adapter.rb +0 -4
  38. data/lib/trestle/admin.rb +18 -1
  39. data/lib/trestle/admin/builder.rb +22 -10
  40. data/lib/trestle/form/automatic.rb +5 -2
  41. data/lib/trestle/form/builder.rb +5 -1
  42. data/lib/trestle/form/field.rb +1 -1
  43. data/lib/trestle/form/fields/form_group.rb +3 -1
  44. data/lib/trestle/form/fields/select.rb +5 -1
  45. data/lib/trestle/form/fields/tag_select.rb +1 -1
  46. data/lib/trestle/form/renderer.rb +1 -1
  47. data/lib/trestle/navigation.rb +11 -5
  48. data/lib/trestle/navigation/item.rb +10 -0
  49. data/lib/trestle/resource.rb +27 -61
  50. data/lib/trestle/resource/builder.rb +15 -14
  51. data/lib/trestle/resource/collection.rb +48 -0
  52. data/lib/trestle/resource/controller.rb +36 -23
  53. data/lib/trestle/scope.rb +13 -3
  54. data/lib/trestle/table.rb +10 -4
  55. data/lib/trestle/table/column.rb +13 -2
  56. data/lib/trestle/table/row.rb +10 -0
  57. data/lib/trestle/version.rb +1 -1
  58. data/trestle.gemspec +1 -0
  59. data/vendor/assets/stylesheets/trestle/magnific-popup.scss +13 -1
  60. metadata +27 -4
  61. data/app/helpers/trestle/dialog_helper.rb +0 -7
@@ -34,10 +34,6 @@ module Trestle
34
34
  instance.destroy
35
35
  end
36
36
 
37
- def unscope(scope)
38
- scope.unfiltered
39
- end
40
-
41
37
  def merge_scopes(scope, other)
42
38
  scope.intersect(other)
43
39
  end
data/lib/trestle/admin.rb CHANGED
@@ -8,7 +8,6 @@ module Trestle
8
8
  class << self
9
9
  attr_accessor :menu
10
10
 
11
- attr_accessor :table
12
11
  attr_accessor :form
13
12
 
14
13
  attr_accessor :additional_routes
@@ -20,6 +19,20 @@ module Trestle
20
19
  @options ||= {}
21
20
  end
22
21
 
22
+ def tables
23
+ @tables ||= {}
24
+ end
25
+
26
+ # Deprecated: Use `tables[:index]` instead
27
+ def table
28
+ tables[:index]
29
+ end
30
+
31
+ # Deprecated: Use `tables[:index]=` instead
32
+ def table=(table)
33
+ tables[:index] = table
34
+ end
35
+
23
36
  def breadcrumbs
24
37
  Breadcrumb::Trail.new(Array(Trestle.config.root_breadcrumbs) + [breadcrumb])
25
38
  end
@@ -75,6 +88,10 @@ module Trestle
75
88
  def railtie_routes_url_helpers(include_path_helpers=true)
76
89
  Trestle.railtie_routes_url_helpers(include_path_helpers)
77
90
  end
91
+
92
+ def build(&block)
93
+ Admin::Builder.build(self, &block)
94
+ end
78
95
  end
79
96
  end
80
97
  end
@@ -11,23 +11,29 @@ module Trestle
11
11
 
12
12
  delegate :helper, :before_action, :after_action, :around_action, to: :@controller
13
13
 
14
- def initialize(name, options={})
14
+ def initialize(admin)
15
+ @admin, @controller = admin, admin.const_get(:AdminController)
16
+ end
17
+
18
+ def self.create(name, options={}, &block)
15
19
  # Create admin subclass
16
- @admin = Class.new(admin_class)
17
- @admin.options = options
20
+ admin = Class.new(admin_class)
21
+ admin.options = options
18
22
 
19
23
  # Define a constant based on the admin name
20
24
  scope = options[:scope] || Object
21
- scope.const_set("#{name.to_s.camelize}Admin", @admin)
25
+ scope.const_set("#{name.to_s.camelize}Admin", admin)
22
26
 
23
27
  # Define admin controller class
24
28
  # This is done using class_eval rather than Class.new so that the full
25
29
  # class name and parent chain is set when Rails' inherited hooks are called.
26
- @admin.class_eval("class AdminController < #{self.class.controller.name}; end")
30
+ admin.class_eval("class AdminController < #{controller.name}; end")
27
31
 
28
32
  # Set a reference on the controller class to the admin class
29
- @controller = @admin.const_get("AdminController")
30
- @controller.instance_variable_set("@admin", @admin)
33
+ controller = admin.const_get(:AdminController)
34
+ controller.instance_variable_set("@admin", admin)
35
+
36
+ admin.build(&block)
31
37
  end
32
38
 
33
39
  def menu(*args, &block)
@@ -38,8 +44,14 @@ module Trestle
38
44
  end
39
45
  end
40
46
 
41
- def table(options={}, &block)
42
- admin.table = Table::Builder.build(options.reverse_merge(admin: admin, sortable: true), &block)
47
+ def table(name_or_options={}, options={}, &block)
48
+ if name_or_options.is_a?(Hash)
49
+ name, options = :index, name_or_options.reverse_merge(admin: admin, sortable: true)
50
+ else
51
+ name = name_or_options
52
+ end
53
+
54
+ admin.tables[name] = Table::Builder.build(options, &block)
43
55
  end
44
56
 
45
57
  def form(options={}, &block)
@@ -47,7 +59,7 @@ module Trestle
47
59
  end
48
60
 
49
61
  def admin(&block)
50
- @admin.singleton_class.class_eval(&block) if block_given?
62
+ @admin.instance_eval(&block) if block_given?
51
63
  @admin
52
64
  end
53
65
 
@@ -13,8 +13,11 @@ module Trestle
13
13
  when :association
14
14
  if attribute.polymorphic?
15
15
  static_field attribute.name do
16
- associated_instance = instance.public_send(attribute.association_name)
17
- admin_link_to format_value(associated_instance), associated_instance
16
+ if associated_instance = instance.public_send(attribute.association_name)
17
+ admin_link_to format_value(associated_instance), associated_instance
18
+ else
19
+ content_tag(:span, I18n.t("admin.format.blank"), class: "blank")
20
+ end
18
21
  end
19
22
  else
20
23
  prompt = I18n.t("admin.form.select.prompt", default: "- Select %{attribute_name} -", attribute_name: admin.human_attribute_name(attribute.association_name))
@@ -10,7 +10,11 @@ module Trestle
10
10
  self.fields = {}
11
11
 
12
12
  def errors(name)
13
- object.errors[name].to_a
13
+ if object.respond_to?(:errors) && object.errors.respond_to?(:[])
14
+ object.errors[name].to_a
15
+ else
16
+ []
17
+ end
14
18
  end
15
19
 
16
20
  def self.register(name, klass)
@@ -39,7 +39,7 @@ module Trestle
39
39
  end
40
40
 
41
41
  def extract_options!
42
- @wrapper = extract_wrapper_options(:help, :label, :hide_label).merge(options.delete(:wrapper))
42
+ @wrapper = extract_wrapper_options(*Fields::FormGroup::WRAPPER_OPTIONS).merge(options.delete(:wrapper))
43
43
  end
44
44
 
45
45
  private
@@ -2,10 +2,12 @@ module Trestle
2
2
  class Form
3
3
  module Fields
4
4
  class FormGroup < Field
5
+ WRAPPER_OPTIONS = [:help, :label, :hide_label]
6
+
5
7
  def render
6
8
  options[:class] << 'has-error' if errors.any?
7
9
 
8
- content_tag(:div, options) do
10
+ content_tag(:div, options.except(*WRAPPER_OPTIONS)) do
9
11
  concat label unless options[:label] == false
10
12
  concat block.call if block
11
13
  concat help_message if options[:help]
@@ -7,7 +7,7 @@ module Trestle
7
7
  def initialize(builder, template, name, choices=nil, options={}, html_options={}, &block)
8
8
  super(builder, template, name, options, &block)
9
9
 
10
- @choices = Choices.new(choices || builder.object.send(name))
10
+ @choices = Choices.new(choices || default_choices)
11
11
  @html_options = default_html_options.merge(html_options)
12
12
  end
13
13
 
@@ -19,6 +19,10 @@ module Trestle
19
19
  Trestle::Options.new(class: ["form-control"], data: { enable_select2: true })
20
20
  end
21
21
 
22
+ def default_choices
23
+ builder.object.send(name) if builder.object
24
+ end
25
+
22
26
  # Allows an array of model instances (or a scope) to be
23
27
  # passed to the select field as the list of choices.
24
28
  class Choices
@@ -7,7 +7,7 @@ module Trestle
7
7
  end
8
8
 
9
9
  def default_html_options
10
- super.merge(multiple: true, class: "tag-select", data: { tags: true, select_on_close: true })
10
+ super.merge(multiple: true, class: "tag-select", data: { tags: true })
11
11
  end
12
12
  end
13
13
  end
@@ -5,7 +5,7 @@ module Trestle
5
5
  include ::ActionView::Helpers::CaptureHelper
6
6
 
7
7
  # Whitelisted helpers will concatenate their result to the output buffer when called.
8
- WHITELISTED_HELPERS = [:row, :col, :render, :tab, :toolbar, :table, :divider]
8
+ WHITELISTED_HELPERS = [:row, :col, :render, :tab, :toolbar, :table, :divider, :h1, :h2, :h3, :h4, :h5, :h6, :panel, :well]
9
9
 
10
10
  # Raw block helpers will pass their block argument directly to the method
11
11
  # without wrapping it in a new output buffer.
@@ -7,12 +7,10 @@ module Trestle
7
7
  autoload :Group
8
8
  autoload :NullGroup, "trestle/navigation/group"
9
9
 
10
- def initialize(blocks)
11
- @blocks = blocks
12
- end
10
+ attr_reader :items
13
11
 
14
- def items
15
- @blocks.map(&:items).flatten
12
+ def initialize(items)
13
+ @items = items
16
14
  end
17
15
 
18
16
  def by_group
@@ -28,6 +26,14 @@ module Trestle
28
26
  sorted.first.first if sorted.any?
29
27
  end
30
28
 
29
+ def visible(context)
30
+ self.class.new(items.select { |item| item.visible?(context) })
31
+ end
32
+
33
+ def self.build(blocks)
34
+ new(blocks.map(&:items).flatten)
35
+ end
36
+
31
37
  private
32
38
  def stable_sort(items)
33
39
  items.sort_by.with_index { |item, i| [item, i] }
@@ -55,6 +55,16 @@ module Trestle
55
55
  Badge.new(options[:badge]) if badge?
56
56
  end
57
57
 
58
+ def visible?(context)
59
+ if options[:if]
60
+ context.instance_exec(&options[:if])
61
+ elsif options[:unless]
62
+ !context.instance_exec(&options[:unless])
63
+ else
64
+ true
65
+ end
66
+ end
67
+
58
68
  class Badge
59
69
  attr_reader :text
60
70
 
@@ -3,11 +3,17 @@ module Trestle
3
3
  extend ActiveSupport::Autoload
4
4
 
5
5
  autoload :Builder
6
+ autoload :Collection
6
7
  autoload :Controller
7
8
 
8
9
  RESOURCE_ACTIONS = [:index, :show, :new, :create, :edit, :update, :destroy]
9
10
  READONLY_ACTIONS = [:index, :show]
10
11
 
12
+ class_attribute :decorator
13
+
14
+ class_attribute :pagination_options
15
+ self.pagination_options = {}
16
+
11
17
  class << self
12
18
  def adapter
13
19
  @adapter ||= Trestle.config.default_adapter.new(self)
@@ -20,10 +26,11 @@ module Trestle
20
26
  # Defines a method that can be overridden with a custom block,
21
27
  # but is otherwise delegated to the adapter instance.
22
28
  def self.adapter_method(name)
23
- attr_writer name
29
+ block_method = :"#{name}_block"
30
+ attr_accessor block_method
24
31
 
25
32
  define_method(name) do |*args|
26
- if override = instance_variable_get("@#{name}")
33
+ if override = public_send(block_method)
27
34
  instance_exec(*args, &override)
28
35
  else
29
36
  adapter.public_send(name, *args)
@@ -31,33 +38,33 @@ module Trestle
31
38
  end
32
39
  end
33
40
 
41
+ # Collection-focused adapter methods
34
42
  adapter_method :collection
43
+ adapter_method :merge_scopes
44
+ adapter_method :sort
45
+ adapter_method :paginate
46
+ adapter_method :finalize_collection
47
+ adapter_method :decorate_collection
48
+ adapter_method :count
49
+
50
+ # Instance-focused adapter methods
35
51
  adapter_method :find_instance
36
52
  adapter_method :build_instance
37
53
  adapter_method :update_instance
38
54
  adapter_method :save_instance
39
55
  adapter_method :delete_instance
40
- adapter_method :to_param
41
56
  adapter_method :permitted_params
42
- adapter_method :decorate_collection
43
- adapter_method :unscope
44
- adapter_method :merge_scopes
45
- adapter_method :count
46
- adapter_method :sort
47
- adapter_method :paginate
57
+
58
+ # Common adapter methods
59
+ adapter_method :to_param
48
60
  adapter_method :human_attribute_name
61
+
62
+ # Automatic tables and forms adapter methods
49
63
  adapter_method :default_table_attributes
50
64
  adapter_method :default_form_attributes
51
65
 
52
- attr_accessor :decorator
53
-
54
66
  def prepare_collection(params)
55
- collection = initialize_collection(params)
56
- collection = apply_scopes(collection, params)
57
- collection = apply_sorting(collection, params)
58
- collection = paginate(collection, params)
59
- collection = decorate_collection(collection)
60
- collection
67
+ Collection.new(self).prepare(params)
61
68
  end
62
69
 
63
70
  def initialize_collection(params)
@@ -68,47 +75,10 @@ module Trestle
68
75
  @scopes ||= {}
69
76
  end
70
77
 
71
- def apply_scopes(collection, params)
72
- unscoped = unscope(collection)
73
-
74
- scopes_for(params).each do |scope|
75
- collection = merge_scopes(collection, scope.apply(unscoped))
76
- end
77
-
78
- collection
79
- end
80
-
81
- def scopes_for(params)
82
- result = []
83
-
84
- if params[:scope] && scopes.has_key?(params[:scope].to_sym)
85
- result << scopes[params[:scope].to_sym]
86
- end
87
-
88
- if result.empty? && default_scope = scopes.values.find(&:default?)
89
- result << default_scope
90
- end
91
-
92
- result
93
- end
94
-
95
78
  def column_sorts
96
79
  @column_sorts ||= {}
97
80
  end
98
81
 
99
- def apply_sorting(collection, params)
100
- return collection unless params[:sort]
101
-
102
- field = params[:sort].to_sym
103
- order = params[:order].to_s.downcase == "desc" ? :desc : :asc
104
-
105
- if column_sorts.has_key?(field)
106
- instance_exec(collection, order, &column_sorts[field])
107
- else
108
- sort(collection, field, order)
109
- end
110
- end
111
-
112
82
  def table
113
83
  super || Table::Automatic.new(self)
114
84
  end
@@ -148,15 +118,11 @@ module Trestle
148
118
  end
149
119
 
150
120
  def return_locations
151
- @return_locations ||= {
152
- create: Proc.new { |instance| path(:show, id: to_param(instance)) },
153
- update: Proc.new { |instance| path(:show, id: to_param(instance)) },
154
- destroy: Proc.new { path(:index) }
155
- }
121
+ @return_locations ||= {}
156
122
  end
157
123
 
158
- def return_location(action, instance=nil)
159
- instance_exec(instance, &return_locations[action])
124
+ def build(&block)
125
+ Resource::Builder.build(self, &block)
160
126
  end
161
127
 
162
128
  private
@@ -22,36 +22,36 @@ module Trestle
22
22
  end
23
23
 
24
24
  def collection(&block)
25
- admin.collection = block
25
+ admin.collection_block = block
26
26
  end
27
27
 
28
28
  def find_instance(&block)
29
- admin.find_instance = block
29
+ admin.find_instance_block = block
30
30
  end
31
31
  alias instance find_instance
32
32
 
33
33
  def build_instance(&block)
34
- admin.build_instance = block
34
+ admin.build_instance_block = block
35
35
  end
36
36
 
37
37
  def update_instance(&block)
38
- admin.update_instance = block
38
+ admin.update_instance_block = block
39
39
  end
40
40
 
41
41
  def save_instance(&block)
42
- admin.save_instance = block
42
+ admin.save_instance_block = block
43
43
  end
44
44
 
45
45
  def delete_instance(&block)
46
- admin.delete_instance = block
46
+ admin.delete_instance_block = block
47
47
  end
48
48
 
49
49
  def to_param(&block)
50
- admin.to_param = block
50
+ admin.to_param_block = block
51
51
  end
52
52
 
53
53
  def params(&block)
54
- admin.permitted_params = block
54
+ admin.permitted_params_block = block
55
55
  end
56
56
 
57
57
  def decorator(decorator)
@@ -59,27 +59,28 @@ module Trestle
59
59
  end
60
60
 
61
61
  def decorate_collection(&block)
62
- admin.decorate_collection = block
62
+ admin.decorate_collection_block = block
63
63
  end
64
64
 
65
65
  def merge_scopes(&block)
66
- admin.merge_scopes = block
66
+ admin.merge_scopes_block = block
67
67
  end
68
68
 
69
69
  def sort(&block)
70
- admin.sort = block
70
+ admin.sort_block = block
71
71
  end
72
72
 
73
73
  def sort_column(column, &block)
74
74
  admin.column_sorts[column.to_sym] = block
75
75
  end
76
76
 
77
- def paginate(&block)
78
- admin.paginate = block
77
+ def paginate(options={}, &block)
78
+ admin.pagination_options = admin.pagination_options.merge(options)
79
+ admin.paginate_block = block
79
80
  end
80
81
 
81
82
  def count(&block)
82
- admin.count = block
83
+ admin.count_block = block
83
84
  end
84
85
 
85
86
  def scope(name, scope=nil, options={}, &block)