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,19 @@
1
+
2
+ module App
3
+ module Panel
4
+ module OptionsConfig
5
+ include Panel::NavArgsOption
6
+
7
+ include Panel::SidebarLinksOption
8
+
9
+ include Panel::ShowParentsOption
10
+
11
+ private
12
+
13
+ def panel_class
14
+ App::Panel::LayoutHeaderSidebar
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,22 @@
1
+
2
+ module App
3
+ module Panel
4
+ module ShowParentsOption
5
+ private
6
+
7
+ def panel_options(renderer=nil, **options)
8
+ super renderer, options.merge(header_right: show_parents(renderer))
9
+ end
10
+
11
+ def show_parents(renderer)
12
+ if respond_to?(:parent) and parent
13
+ parent_list = Templet::Utils::ListModelParents.new
14
+ .in_html(parent, [*grand_parent])
15
+
16
+ in_html_list(renderer, parent_list, html_class: :stacked)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,24 @@
1
+
2
+ module App
3
+ module Panel
4
+ module SidebarLinksOption
5
+ private
6
+
7
+ def panel_options(renderer=nil, **options)
8
+ super renderer, options.merge(sidebar_cols: nil,
9
+ sidebar_content: sidebar_links(renderer))
10
+ end
11
+
12
+ def sidebar_links(renderer)
13
+ Templet::Utils::LinkSetFactoryWrapper
14
+ .new(renderer, action, sidebar_links_class)
15
+ #.(:toolbar, stacked: true)
16
+ end
17
+
18
+ def sidebar_links_class
19
+ Templet::Links::BsLinkSetNavigation
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,12 @@
1
+
2
+ # Produces output for all JSON requests
3
+ module Templet::JsonRenderingHelpers
4
+ private
5
+
6
+ def json_for(model, status: :ok, in_error: false)
7
+ model, status = model.errors, :unprocessable_entity if in_error
8
+
9
+ { json: model, status: status, location: "#{controller_name}/#{action_name}" }
10
+ end
11
+ end
12
+
@@ -0,0 +1,53 @@
1
+
2
+ # Helper methods used in controller actions to render views
3
+ module Templet::RenderingHelpers
4
+ include ActionView::Helpers::JavaScriptHelper
5
+
6
+ private
7
+
8
+ # Calls a Viewer class
9
+ def viewer(action, model_name=nil, **options)
10
+ options = viewer_options.merge(options)
11
+
12
+ model_name ||= options.delete(:model_name) || model_name()
13
+
14
+ Templet::ViewerCallString.new(action, model_name, **options)
15
+ end
16
+
17
+ # For JS requests
18
+ def viewer_to_s(*args, **options)
19
+ options.reverse_merge! wrapper: render_target
20
+
21
+ render_to_string inline viewer(*args, **options)
22
+ end
23
+ def js(html, target=nil)
24
+ target ||= "#panel-#{render_target}"
25
+
26
+ { js: "$('#{target}').html('#{escape_javascript html}')" }
27
+ end
28
+
29
+ # Invokes the Rails-supplied render option
30
+ def inline(ruby_code, has_layout=false)
31
+ { inline: "<%= (#{ruby_code}).html_safe %>", layout: has_layout }
32
+ end
33
+
34
+ # The following methods are defaults that can be overridden in the controller
35
+
36
+ # Used by the Viewer extensively, it points to the main instance variable,
37
+ # is shown in default headings, etc.
38
+ def model_name
39
+ controller_name.singularize
40
+ end
41
+
42
+ # Default options passed into the Viewer by each controller action
43
+ # You can specify the name of a controller's parent model here
44
+ def viewer_options
45
+ { controller: params[:controller] }
46
+ end
47
+
48
+ # The HTML id (within the panel) into which JS (remote) output is inserted
49
+ def render_target
50
+ :outer
51
+ end
52
+ end
53
+
@@ -0,0 +1,144 @@
1
+
2
+ module Templet
3
+ # Constructs a string of Ruby that, when executed, returns a string of HTML.
4
+ #
5
+ # It calls a method, named by the given +action+ parameter, on an instance
6
+ # of the type: Templet::ViewerRest.
7
+ #
8
+ # The resultant code statement is executed within the view context
9
+ # i.e. as if it's called from the inside of an ERb partial.
10
+ #
11
+ # It takes this circuitous route to pass various items from the controller
12
+ # to the view e.g. instance variables, model names, link set controls.
13
+ class ViewerCallString
14
+ attr_accessor :action, :controller, :model_name, :parent,
15
+ :models, :use_model_name,
16
+ :grand_parent, :variables, :wrapper
17
+
18
+ attr_accessor :viewer_class
19
+ #
20
+ # +action+:: The REST action, i.e. the controller method name
21
+ #
22
+ # +controller+:: The name without the suffix 'Controller', but may be prefixed with a scope/namespace
23
+ #
24
+ # +model_name+:: The model name, mirroring the model's instance variable
25
+ #
26
+ # +parent+:: An actual instance of a model's dependent, i.e. the belongs_to
27
+ #
28
+ # +models+:: A list of models, used in the index action, it names the instance variable
29
+ #
30
+ # +use_model_name+:: If true, no instance variable of the model is defined, e.g. in index and new
31
+ #
32
+ # +use_base+:: If true, it will render the view using the superclass, Templet::ApplicationRestViewer
33
+ #
34
+ # +class_name+:: The name of the Viewer class
35
+ #
36
+ # +module_name+:: The name of the Viewer module
37
+ #
38
+ # +grand_parent+:: The name of the parent's parent, for 'Back' buttons
39
+ #
40
+ # +variables+:: A list of instance variables to be passed into the view (no @ prefix)
41
+ #
42
+ # +wrapper+:: For specifying an HTML id, i.e. the target of a JS request
43
+ #
44
+ def initialize(action, model_name, controller: nil,
45
+ parent: nil,
46
+ models: nil,
47
+ use_model_name: nil,
48
+ use_base: false,
49
+ class_name: nil,
50
+ module_name: nil,
51
+ grand_parent: nil,
52
+ variables: [],
53
+ wrapper: nil)
54
+ self.action = action
55
+
56
+ self.controller = controller
57
+
58
+ self.model_name = model_name
59
+
60
+ self.parent = parent
61
+
62
+ self.models = models
63
+
64
+ # Specify a model name as opposed to an instance in an instance variable
65
+ # It's because there is no model in the actions 'index' and 'new'
66
+ self.use_model_name = if use_model_name.nil?
67
+ models ? true : false
68
+ else
69
+ use_model_name
70
+ end
71
+
72
+ self.grand_parent = grand_parent
73
+
74
+ self.variables = variables
75
+
76
+ self.wrapper = wrapper
77
+
78
+ self.viewer_class = ViewerCallStringClass.new(controller, model_name,
79
+ use_base,
80
+ class_name, module_name)
81
+ end
82
+
83
+ def call(action=action())
84
+ "#{viewer_class}.new(#{args}#{opts}).#{action}"
85
+ end
86
+
87
+ alias to_s call
88
+
89
+ private
90
+
91
+ # Arguments
92
+
93
+ def args
94
+ "self" << model_arg << parent_arg
95
+ end
96
+
97
+ def model_arg
98
+ prefix = use_model_name ? ':' : '@'
99
+
100
+ to_arg(model_name, prefix)
101
+ end
102
+
103
+ def parent_arg
104
+ to_arg(parent, '@')
105
+ end
106
+
107
+ def to_arg(value_name, value_prefix='')
108
+ value_name ? ", #{value_prefix}#{value_name}" : ''
109
+ end
110
+
111
+ # Options
112
+
113
+ def opts
114
+ opts = controller_opt << models_opt << grand_parent_opt << wrapper_opt
115
+
116
+ variables.reduce(opts) {|acca, variable| acca << to_opt(variable) }
117
+ end
118
+
119
+ def controller_opt
120
+ controller ? to_opt("'#{controller}'", :controller, '') : ''
121
+ end
122
+
123
+ def models_opt
124
+ to_opt(models, :models, models.to_s.start_with?('@') ? '' : '@')
125
+ end
126
+
127
+ def grand_parent_opt
128
+ to_opt(grand_parent, :grand_parent)
129
+ end
130
+
131
+ def wrapper_opt
132
+ to_opt((wrapper == false ? :none : wrapper), :wrapper)
133
+ end
134
+
135
+ def to_opt(value_name, key_name=nil, value_prefix=nil)
136
+ value_prefix ||= key_name ? ':' : '@'
137
+
138
+ key_name ||= value_name
139
+
140
+ value_name ? ", #{key_name}: #{value_prefix}#{value_name}" : ''
141
+ end
142
+ end
143
+ end
144
+
@@ -0,0 +1,65 @@
1
+
2
+ module Templet
3
+ # Returns the name (as a string) of the Viewer class
4
+ # It's used internally by ViewerCallString
5
+ class ViewerCallStringClass < Struct.new(:controller, :model_name,
6
+ :use_base, :class_name,
7
+ :module_name)
8
+ APP_MODULE = 'App' # Base namespace of the application subclasses
9
+
10
+ VIEWER_CLASS_SUFFIX = "Viewer"
11
+
12
+ VIEWER_BASE_CLASS = APP_MODULE + '::Base' + VIEWER_CLASS_SUFFIX
13
+
14
+ def call
15
+ if use_base
16
+ VIEWER_BASE_CLASS
17
+ elsif String === class_name
18
+ class_name
19
+ else
20
+ subclass_name
21
+ end
22
+ end
23
+
24
+ alias to_s call
25
+
26
+ private
27
+
28
+ # Reverts to the base class if there is no Viewer class that is either
29
+ # explicitly given or is implicitly derived, e.g. from the controller's name
30
+ def subclass_name
31
+ subclass_name = application_subclass
32
+
33
+ if (subclass_name.constantize rescue false)
34
+ subclass_name
35
+ else
36
+ VIEWER_BASE_CLASS
37
+ end
38
+ end
39
+
40
+ def application_subclass
41
+ APP_MODULE + controller_module +
42
+ (class_name || controller_name || model_name).to_s.classify +
43
+ VIEWER_CLASS_SUFFIX
44
+ end
45
+
46
+ def controller_name
47
+ if controller
48
+ controller.to_s.split('/').last
49
+ end
50
+ end
51
+
52
+ def controller_module
53
+ if module_name
54
+ "#{module_name.to_s.classify}::"
55
+ elsif controller
56
+ controller.to_s.split('/')[0..-2].reduce('') do |acca, name|
57
+ acca << '::' << name.classify
58
+ end << '::'
59
+ else
60
+ '::'
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,121 @@
1
+
2
+ # Used in controller actions to dispatch the HTML, JS & JSON formats
3
+ module Templet::ViewerResponders
4
+ include Templet::RenderingHelpers
5
+
6
+ include Templet::JsonRenderingHelpers
7
+
8
+ private
9
+
10
+ def respond_to_index(models_name=nil, models=nil, **options)
11
+ models_name ||= model_name.to_s.pluralize
12
+
13
+ respond_to do |format|
14
+ format.html do
15
+ render inline viewer(:index, **merge_models(options, models_name))
16
+ end
17
+
18
+ format.js do
19
+ render js viewer_to_s(:index, **merge_models(options, models_name))
20
+ end
21
+
22
+ format.json do
23
+ render json_for(models || instance_var(models_name))
24
+ end
25
+ end
26
+ end
27
+
28
+ def respond_to_action(action, model=nil, **options)
29
+ respond_to do |format|
30
+ format.html { render inline viewer(action, **options) }
31
+
32
+ format.js { render js viewer_to_s(action, **options) }
33
+
34
+ format.json { render json_for(model || instance_var(model_name)) }
35
+ end
36
+ end
37
+
38
+ def respond_to_show(*args)
39
+ respond_to_action :show, *args
40
+ end
41
+ def respond_to_new(*args)
42
+ respond_to_action :new, *args
43
+ end
44
+ def respond_to_edit(*args)
45
+ respond_to_action :edit, *args
46
+ end
47
+
48
+ def respond_to_save_success(model, redirect=model, status=nil,
49
+ notice=nil, **options)
50
+ status ||= model.id_previously_changed? ? :created : :ok
51
+
52
+ notice ||= default_notice(model)
53
+
54
+ respond_to do |format|
55
+ format.html do
56
+ redirect_to redirect, notice: notice
57
+ end
58
+
59
+ format.js do
60
+ set_message(notice)
61
+
62
+ render js viewer_to_s(:show, **options)
63
+ end
64
+
65
+ format.json { render json_for(model, status: status) }
66
+ end
67
+ end
68
+
69
+ def respond_to_save_failure(action, model, **options)
70
+ respond_to do |format|
71
+ format.html { render inline viewer(action, **options) }
72
+
73
+ format.js { render js viewer_to_s(action, **options) }
74
+
75
+ format.json { render json_for(model, in_error: true) }
76
+ end
77
+ end
78
+
79
+ def respond_to_destroy(model, redirect=model, notice=nil)
80
+ notice ||= default_notice(model, "deleted")
81
+
82
+ respond_to do |format|
83
+ format.html { redirect_to redirect, notice: notice }
84
+
85
+ format.js do
86
+ set_message(notice)
87
+
88
+ index
89
+ end
90
+
91
+ format.json { head :no_content }
92
+ end
93
+ end
94
+
95
+ def merge_models(options, models_name)
96
+ options.merge models: to_iv(models_name)
97
+ end
98
+
99
+ def instance_var(name)
100
+ instance_variable_get to_iv(name)
101
+ end
102
+
103
+ def to_iv(name)
104
+ name.to_s.start_with?('@') ? name : "@#{name}".to_sym
105
+ end
106
+
107
+ def default_notice(model, suffix=nil, prefix=nil)
108
+ "#{prefix || notice_prefix} #{model} #{suffix || notice_suffix}."
109
+ end
110
+ def notice_prefix
111
+ model_name.to_s.capitalize
112
+ end
113
+ def notice_suffix
114
+ action_name + 'd'
115
+ end
116
+
117
+ def set_message(text, key=:notice)
118
+ flash.now[key] = text
119
+ end
120
+ end
121
+