templet_rails 0.1.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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +1059 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/lib/generators/templet/controller/USAGE +13 -0
  11. data/lib/generators/templet/controller/controller_generator.rb +106 -0
  12. data/lib/generators/templet/core/USAGE +23 -0
  13. data/lib/generators/templet/core/core_generator.rb +128 -0
  14. data/lib/generators/templet/core_rspec/USAGE +16 -0
  15. data/lib/generators/templet/core_rspec/core_rspec_generator.rb +65 -0
  16. data/lib/generators/templet/destroy/USAGE +19 -0
  17. data/lib/generators/templet/destroy/destroy_generator.rb +126 -0
  18. data/lib/generators/templet/routes/USAGE +14 -0
  19. data/lib/generators/templet/routes/routes_generator.rb +64 -0
  20. data/lib/generators/templet/rspec/USAGE +10 -0
  21. data/lib/generators/templet/rspec/rspec_generator.rb +124 -0
  22. data/lib/generators/templet/scaffold/USAGE +22 -0
  23. data/lib/generators/templet/scaffold/scaffold_generator.rb +75 -0
  24. data/lib/generators/templet/shared/actions_option.rb +24 -0
  25. data/lib/generators/templet/shared/add_routes_option.rb +19 -0
  26. data/lib/generators/templet/shared/child_option.rb +23 -0
  27. data/lib/generators/templet/shared/comment_tests_option.rb +19 -0
  28. data/lib/generators/templet/shared/core_helpers.rb +48 -0
  29. data/lib/generators/templet/shared/grand_parent_option.rb +20 -0
  30. data/lib/generators/templet/shared/model_fields.rb +55 -0
  31. data/lib/generators/templet/shared/model_option.rb +25 -0
  32. data/lib/generators/templet/shared/parent_option.rb +20 -0
  33. data/lib/generators/templet/templates/controller.rb.erb +108 -0
  34. data/lib/generators/templet/templates/core/app/base_viewer.rb +7 -0
  35. data/lib/generators/templet/templates/core/app/link_sets/navbar.rb +12 -0
  36. data/lib/generators/templet/templates/core/app/panel/flash_messages.rb +33 -0
  37. data/lib/generators/templet/templates/core/app/panel/layout_base.rb +40 -0
  38. data/lib/generators/templet/templates/core/app/panel/layout_header.rb +22 -0
  39. data/lib/generators/templet/templates/core/app/panel/layout_header_sidebar.rb +18 -0
  40. data/lib/generators/templet/templates/core/app/panel/nav.rb +55 -0
  41. data/lib/generators/templet/templates/core/app/panel/nav_args_option.rb +30 -0
  42. data/lib/generators/templet/templates/core/app/panel/options_config.rb +19 -0
  43. data/lib/generators/templet/templates/core/app/panel/show_parents_option.rb +22 -0
  44. data/lib/generators/templet/templates/core/app/panel/sidebar_links_option.rb +24 -0
  45. data/lib/generators/templet/templates/core/controllers/.keep +0 -0
  46. data/lib/generators/templet/templates/core/controllers/json_rendering_helpers.rb +12 -0
  47. data/lib/generators/templet/templates/core/controllers/rendering_helpers.rb +53 -0
  48. data/lib/generators/templet/templates/core/controllers/viewer_call_string.rb +144 -0
  49. data/lib/generators/templet/templates/core/controllers/viewer_call_string_class.rb +65 -0
  50. data/lib/generators/templet/templates/core/controllers/viewer_responders.rb +121 -0
  51. data/lib/generators/templet/templates/core/helpers/app.rb +4 -0
  52. data/lib/generators/templet/templates/core/helpers/templet_helper.rb +10 -0
  53. data/lib/generators/templet/templates/core/spec/support/apis/api_helper.rb +13 -0
  54. data/lib/generators/templet/templates/core/spec/support/apis/shared_examples_a_json_controller.rb +196 -0
  55. data/lib/generators/templet/templates/core/spec/support/core/model_parent_helpers.rb +36 -0
  56. data/lib/generators/templet/templates/core/spec/support/core/rest_link_procs_assignments.rb +43 -0
  57. data/lib/generators/templet/templates/core/spec/support/core/rest_link_procs_helpers.rb +15 -0
  58. data/lib/generators/templet/templates/core/spec/support/viewer/partial_test_helpers.rb +32 -0
  59. data/lib/generators/templet/templates/core/spec/support/viewer/shared_examples_a_viewer.rb +105 -0
  60. data/lib/generators/templet/templates/core/spec/templet/forms/bs_form_errors_spec.rb +18 -0
  61. data/lib/generators/templet/templates/core/spec/templet/forms/bs_form_spec.rb +58 -0
  62. data/lib/generators/templet/templates/core/spec/templet/layout/html_rails_spec.rb +64 -0
  63. data/lib/generators/templet/templates/core/spec/templet/links/bs_link_set_collection_spec.rb +79 -0
  64. data/lib/generators/templet/templates/core/spec/templet/links/rest_link_procs_params_spec.rb +68 -0
  65. data/lib/generators/templet/templates/core/spec/templet/links/rest_link_procs_parents_spec.rb +78 -0
  66. data/lib/generators/templet/templates/core/spec/templet/links/rest_link_procs_spec.rb +140 -0
  67. data/lib/generators/templet/templates/core/templet/constants.rb +54 -0
  68. data/lib/generators/templet/templates/core/templet/forms/bs_form.rb +73 -0
  69. data/lib/generators/templet/templates/core/templet/forms/bs_form_errors.rb +38 -0
  70. data/lib/generators/templet/templates/core/templet/forms/bs_form_field.rb +114 -0
  71. data/lib/generators/templet/templates/core/templet/forms/bs_form_group.rb +115 -0
  72. data/lib/generators/templet/templates/core/templet/layouts/html_rails.rb +36 -0
  73. data/lib/generators/templet/templates/core/templet/links.rb +28 -0
  74. data/lib/generators/templet/templates/core/templet/links/bs_btn_class.rb +61 -0
  75. data/lib/generators/templet/templates/core/templet/links/bs_link_set_base.rb +168 -0
  76. data/lib/generators/templet/templates/core/templet/links/bs_link_set_collection.rb +21 -0
  77. data/lib/generators/templet/templates/core/templet/links/bs_link_set_navigation.rb +62 -0
  78. data/lib/generators/templet/templates/core/templet/links/rest_link_procs.rb +163 -0
  79. data/lib/generators/templet/templates/core/templet/links/rest_link_procs_parent.rb +39 -0
  80. data/lib/generators/templet/templates/core/templet/links/rest_link_procs_sets.rb +30 -0
  81. data/lib/generators/templet/templates/core/templet/links/rest_link_text.rb +102 -0
  82. data/lib/generators/templet/templates/core/templet/links/rest_path.rb +90 -0
  83. data/lib/generators/templet/templates/core/templet/mixins.rb +9 -0
  84. data/lib/generators/templet/templates/core/templet/mixins/bs.rb +11 -0
  85. data/lib/generators/templet/templates/core/templet/mixins/bs/grid.rb +53 -0
  86. data/lib/generators/templet/templates/core/templet/mixins/bs/lists.rb +77 -0
  87. data/lib/generators/templet/templates/core/templet/mixins/field_procs.rb +140 -0
  88. data/lib/generators/templet/templates/core/templet/mixins/html_presenters.rb +82 -0
  89. data/lib/generators/templet/templates/core/templet/require_all.rb +45 -0
  90. data/lib/generators/templet/templates/core/templet/utils/html_search_form.rb +53 -0
  91. data/lib/generators/templet/templates/core/templet/utils/link_set_factory.rb +46 -0
  92. data/lib/generators/templet/templates/core/templet/utils/link_set_factory_wrapper.rb +53 -0
  93. data/lib/generators/templet/templates/core/templet/utils/list_model_parents.rb +27 -0
  94. data/lib/generators/templet/templates/core/templet/utils/navbar_form.rb +26 -0
  95. data/lib/generators/templet/templates/core/templet/utils/selected_wrapper.rb +40 -0
  96. data/lib/generators/templet/templates/core/templet/viewer.rb +11 -0
  97. data/lib/generators/templet/templates/core/templet/viewer/meta_model.rb +66 -0
  98. data/lib/generators/templet/templates/core/templet/viewer/meta_model_defaults.rb +56 -0
  99. data/lib/generators/templet/templates/core/templet/viewer/presenters.rb +44 -0
  100. data/lib/generators/templet/templates/core/templet/viewer/rest_actions.rb +24 -0
  101. data/lib/generators/templet/templates/core/templet/viewer_base.rb +111 -0
  102. data/lib/generators/templet/templates/core/templet/viewer_rest.rb +144 -0
  103. data/lib/generators/templet/viewer/USAGE +10 -0
  104. data/lib/generators/templet/viewer/viewer_generator.rb +86 -0
  105. data/lib/templet_rails.rb +5 -0
  106. data/lib/templet_rails/version.rb +3 -0
  107. data/templet_rails.gemspec +36 -0
  108. metadata +178 -0
@@ -0,0 +1,56 @@
1
+
2
+ module Templet
3
+ module Viewer
4
+ # Provides controls for scaffolding defaults for index, show, and forms
5
+ module MetaModelDefaults
6
+ private
7
+
8
+ # Html default display controls - for overriding
9
+
10
+ def index_controls(*args)
11
+ procs_by_name = args.pop if Hash === args.last
12
+
13
+ link_procs = args.pop || default_rest_link_procs
14
+
15
+ procs_by_name ||= hash_field_name_by_proc_call_method_default
16
+
17
+ forward_link(link_procs, to: procs_by_name)
18
+
19
+ procs_by_name.merge(link_procs.member_link_hash)
20
+ end
21
+
22
+ def default_rest_link_procs
23
+ rest_link_procs(%i(default sm), remote?)
24
+ end
25
+
26
+ def hash_field_name_by_proc_call_method_default
27
+ hash_field_name_by_proc_call_method(meta_model.listing_names)
28
+ end
29
+
30
+ def forward_link(link_procs=rest_link_procs(:link), to: nil)
31
+ if forward
32
+ text = forward.to_s.capitalize
33
+
34
+ link = link_procs.index_link(text, name: forward)
35
+
36
+ (block_given? ? yield(link) : link).tap do |link_proc|
37
+ to[:"_#{forward}"] = link_proc if to
38
+ end
39
+ end
40
+ end
41
+
42
+ def show_controls(field_names=meta_model.names)
43
+ hash_field_name_by_value_proc field_names
44
+ end
45
+
46
+ def form_inputs(field, group=nil)
47
+ meta_model.input_names.map do |field_name|
48
+ input_method_name = meta_model.input(field_name)
49
+
50
+ field.send(input_method_name, field_name)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
@@ -0,0 +1,44 @@
1
+
2
+ module Templet
3
+ module Viewer
4
+ # For rendering HTML tables and forms
5
+ # Shortcuts to method calls in Templet::Mixins:HtmlPresenters
6
+ # Supports Kaminari paging - as long as the gem's installed
7
+ module Presenters
8
+ def list(renderer)
9
+ html_definition_list(renderer, show_controls, model)
10
+ end
11
+
12
+ def form(renderer)
13
+ path = Links::RestPath.new model, parent, scope: controllers_path,
14
+ controller: controllers_name
15
+
16
+ html_form(renderer, url: path, height: form_height)
17
+ end
18
+
19
+ def table(renderer, records=models, footer: nil)
20
+ footer ||= pagination(renderer, records)
21
+
22
+ html_table(renderer, index_controls, records, opaque_heading: model,
23
+ opaque_row: parent,
24
+ footer: footer)
25
+ end
26
+
27
+ private
28
+
29
+ def pagination(renderer, records)
30
+ if pagination? and records.respond_to? :current_page
31
+ renderer.call do
32
+ [ paginate(records),
33
+ em(page_entries_info(records), 'small pull-right') ]
34
+ end
35
+ end
36
+ end
37
+
38
+ def pagination?
39
+ defined? Kaminari
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,24 @@
1
+
2
+ module Templet
3
+ module Viewer
4
+ # Dispatches REST actions
5
+ module RestActions
6
+ def index(records=models, action=nil)
7
+ wrap_action(action || __method__) {|renderer| table renderer, records }
8
+ end
9
+
10
+ def show
11
+ wrap_action(__method__) {|renderer| list renderer }
12
+ end
13
+
14
+ def new
15
+ wrap_action(__method__) {|renderer| form renderer }
16
+ end
17
+
18
+ def edit
19
+ wrap_action(__method__) {|renderer| form renderer }
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,111 @@
1
+
2
+ module Templet
3
+ # An abstract superclass, either of *ViewerRest*
4
+ # Or of your own custom class if you're not using a REST-based interface
5
+ class ViewerBase
6
+ include Mixins
7
+
8
+ attr_accessor :contexts, :locals
9
+
10
+ attr_accessor :action, :wrapper
11
+
12
+ def initialize(contexts, *, **locals)
13
+ self.contexts = [ contexts ].flatten
14
+
15
+ self.locals = locals
16
+
17
+ self.wrapper = locals[:wrapper] || :layout
18
+ end
19
+
20
+ private
21
+
22
+ # Layout and panel
23
+
24
+ # Called within a method that adds or overrides an action
25
+ def wrap_action(action=nil)
26
+ self.action = (action || caller_locations(1, 1)[0].label).to_sym
27
+
28
+ wrap_around {|renderer| yield renderer }
29
+ end
30
+
31
+ # For rendering e.g. JS - use a layout or a panel or have nothing surrounding
32
+ def wrap_around(&block)
33
+ case wrapper
34
+ when :none, :inner
35
+ new_renderer.call {|renderer| yield renderer }
36
+ when :panel, :outer
37
+ panel(new_renderer, &block)
38
+ else
39
+ layout(&block)
40
+ end
41
+ end
42
+
43
+ # The layout surrounds everything
44
+ def layout(action=nil, &block)
45
+ self.action ||= action
46
+
47
+ layout_class.new(*contexts, **locals).call(*layout_args) do |renderer|
48
+ panel renderer, &block
49
+ end
50
+ end
51
+
52
+ # A panel is a sub-layout within the main layout (just above)
53
+ def panel(renderer)
54
+ panel_class.new(renderer).(*panel_args(renderer)) do |renderer|
55
+ yield renderer
56
+ end
57
+ end
58
+
59
+ def new_renderer
60
+ Templet::Renderer.new(*contexts, **locals)
61
+ end
62
+
63
+ # Used chiefly in menus to check if a link is active
64
+ def action?(current_action)
65
+ current_action.to_sym == action.to_sym
66
+ end
67
+
68
+ # Defaults - for overriding
69
+
70
+ def layout_class
71
+ Layouts::HtmlRails
72
+ end
73
+
74
+ def layout_args
75
+ return page_title, ''
76
+ end
77
+
78
+ def page_title
79
+ Rails.app_class.name.split(/::/).first
80
+ end
81
+
82
+ def panel_class
83
+ Templet::Component::Partial
84
+ end
85
+
86
+ def panel_args(renderer)
87
+ return action, panel_options(renderer)
88
+ end
89
+
90
+ def panel_options(renderer=nil, **options)
91
+ options.reverse_merge panel_options_default
92
+ end
93
+
94
+ def panel_options_default
95
+ { title: panel_title, subtitle: panel_subtitle }
96
+ end
97
+
98
+ def panel_subtitle
99
+ action.to_s.capitalize
100
+ end
101
+
102
+ def panel_title
103
+ if controller
104
+ controller.to_s.titleize
105
+ else
106
+ self.class.name.sub('Viewer', '').split(/::/).last
107
+ end
108
+ end
109
+ end
110
+ end
111
+
@@ -0,0 +1,144 @@
1
+
2
+ #require 'app/panel/main'
3
+
4
+ module Templet
5
+ # The principal class which carries out the default HTML rendering
6
+ # It is an entry point, even though it's mostly is used as a superclass
7
+ class ViewerRest < ViewerBase
8
+ include Viewer
9
+
10
+ include Mixins
11
+ include Mixins::Bs
12
+
13
+ include Templet::Constants
14
+
15
+ attr_accessor :model, :parent, :grand_parent, :controller, :models
16
+
17
+ attr_accessor :meta_model
18
+
19
+ def initialize(contexts, model=nil, parent=nil, **locals)
20
+ self.model = model
21
+
22
+ self.parent = parent || locals[:parent]
23
+
24
+ self.controller = locals[:controller]&.to_s
25
+
26
+ self.grand_parent = locals[:grand_parent] || backward
27
+
28
+ self.models = locals[:models] || []
29
+
30
+ self.meta_model = Viewer::MetaModel.new(model)
31
+
32
+ locals.merge!(view_variables)
33
+
34
+ super contexts, **locals
35
+ end
36
+
37
+ private
38
+
39
+ # These values become global methods for accessing inside the view
40
+ def view_variables
41
+ { model: model,
42
+ parent: parent,
43
+ controllers_name: controllers_name,
44
+ controllers_path: controllers_path,
45
+ grand_parent: grand_parent,
46
+ forward: forward,
47
+ link_set_args_proc: link_set_args_proc }
48
+ end
49
+
50
+ def controllers_name
51
+ if controller
52
+ controller_name = controller.split('/').last.to_s
53
+
54
+ if controller_name != plural_model
55
+ return controller_name
56
+ end
57
+ end
58
+ end
59
+
60
+ def controllers_path
61
+ if controller
62
+ paths = controller.split('/').tap {|paths| paths.pop }
63
+
64
+ if paths.any?
65
+ paths.join('/')
66
+ end
67
+ end
68
+ end
69
+
70
+ def plural_model
71
+ if model.respond_to?(:model_name)
72
+ model.model_name.plural
73
+ else
74
+ model.to_s.pluralize
75
+ end
76
+ end
77
+
78
+ # Returns the arguments used to construct Templet::Links::BsLinkSetBase
79
+ # whose subclasses render REST links in menus and button groups
80
+ def link_set_args_proc
81
+ -> (renderer, options) {
82
+ options.reverse_merge! scope: namespace,
83
+ controller: controllers_name,
84
+ remote: remote?,
85
+ verify_links: verify_rest_links?
86
+
87
+ [ renderer, model, parent, grand_parent, forward, **options ]
88
+ }
89
+ end
90
+
91
+ def namespace
92
+ scope or controllers_path
93
+ end
94
+
95
+ # The remaining methods are for overriding, there are more in the superclass
96
+
97
+ # Layouts
98
+
99
+ def panel_title
100
+ if model
101
+ case model
102
+ when String, Symbol
103
+ model.to_s
104
+ else
105
+ model.class.name
106
+ end
107
+ else
108
+ super.sub('Rest', '')
109
+ end.humanize.pluralize
110
+ end
111
+
112
+ # General
113
+
114
+ # Height of form fields
115
+ def form_height
116
+ 'md'
117
+ end
118
+
119
+ # Use JS (AJAX) for form submissions, and REST links of the index table
120
+ def remote?
121
+ end
122
+
123
+ # Name of model's grand parent (underscored)
124
+ # Used for 'Back' buttons
125
+ def backward
126
+ end
127
+
128
+ # Name (underscored) of model's child
129
+ # Used for (index) links that list children
130
+ def forward
131
+ end
132
+
133
+ # For use in specifying a namespace in links
134
+ def scope
135
+ end
136
+
137
+ # Set to true if the controller does not support all REST actions,
138
+ # for example, if just index and show are available
139
+ def verify_rest_links?
140
+ false
141
+ end
142
+ end
143
+ end
144
+
@@ -0,0 +1,10 @@
1
+ Description:
2
+ Generates a single Viewer subclass, which is called by the controller
3
+ to actually render the view. In this class you may add your own
4
+ modifications that alter the default behaviour.
5
+
6
+ Example:
7
+ rails generate templet:viewer user --child kid --actions index show
8
+
9
+ This will create:
10
+ app/helpers/app/user_viewer.rb
@@ -0,0 +1,86 @@
1
+
2
+ require_relative '../shared/child_option'
3
+ require_relative '../shared/actions_option'
4
+
5
+ class Templet::ViewerGenerator < Rails::Generators::NamedBase
6
+ include Shared::ChildOption
7
+ include Shared::ActionsOption
8
+
9
+ source_root File.expand_path('./', __FILE__)
10
+ desc
11
+
12
+ def create_viewer_subclass
13
+ create_file "app/helpers/app/#{file_path}_viewer.rb", class_definition
14
+ end
15
+
16
+ private
17
+
18
+ def have_actions?
19
+ actions
20
+ end
21
+
22
+ def index_controls_method
23
+ return unless have_actions?
24
+
25
+ %Q[
26
+ # For showing links to member actions (show edit destroy) in table rows
27
+ def index_controls
28
+ links = default_rest_link_procs.member_link_hash %i(#{member_actions})
29
+
30
+ hash_field_name_by_proc_call_method_default.merge links
31
+ end
32
+ ]
33
+ end
34
+
35
+ def member_actions
36
+ (%w(show edit delete destroy) & actions).map {|action| "_#{action}" }
37
+ .join(' ')
38
+ end
39
+
40
+ def forward_method
41
+ return unless child
42
+
43
+ %Q[
44
+ # Used in button groups for links to a model's children
45
+ def forward
46
+ #{child? ? ' :' + child.pluralize : ''}
47
+ end
48
+ ]
49
+ end
50
+
51
+ def verify_rest_links_method
52
+ return unless have_actions?
53
+
54
+ %Q[
55
+ # Checks whether a REST link is actually defined before displaying it
56
+ def verify_rest_links?
57
+ true
58
+ end
59
+ ]
60
+ end
61
+
62
+ def class_definition
63
+ %Q[
64
+ module App
65
+ class #{class_name}Viewer < BaseViewer
66
+ #{index_controls_method}
67
+ private
68
+ #{forward_method}
69
+ #{verify_rest_links_method}
70
+ # You can override the following methods:
71
+ #
72
+ # remote? For AJAX links and forms
73
+ # form_height For the field height on the form (sm md lg)
74
+ # forward For the model's principal has_many relationship
75
+ # backward For the model's grand-parent
76
+ # This can be overriden in the controller
77
+ #
78
+ # There are also a number of others, mostly to do with the layout
79
+ #
80
+ # For more info see: app/helpers/templet/viewer_base.rb
81
+ # app/helpers/templet/viewer_rest.rb
82
+ end
83
+ end
84
+ ]
85
+ end
86
+ end