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,4 @@
1
+
2
+ module App
3
+ end
4
+
@@ -0,0 +1,10 @@
1
+
2
+ #require 'templet/require_all'
3
+ #require 'app'
4
+
5
+ module TempletHelper
6
+ def icon(name)
7
+ "<span class='glyphicon glyphicon-#{name}'></span>".html_safe
8
+ end
9
+ end
10
+
@@ -0,0 +1,13 @@
1
+
2
+ module ApiHelper
3
+ include Rack::Test::Methods
4
+
5
+ def app
6
+ Rails.application
7
+ end
8
+ end
9
+
10
+ RSpec.configure do |config|
11
+ config.include ApiHelper, type: :api
12
+ end
13
+
@@ -0,0 +1,196 @@
1
+
2
+ require 'json'
3
+
4
+ unless defined? UNPROCESSABLE_ENTITY
5
+ STATUS_SUCCESS, STATUS_CREATED, STATUS_NO_CONTENT = 200, 201, 204
6
+
7
+ UNPROCESSABLE_ENTITY = 422
8
+ end
9
+
10
+ shared_examples 'a json controller' do |actions|
11
+ # Naming the model and its belongs_to dependent
12
+ let(:model_name) { new_model.model_name }
13
+
14
+ let(:model_singular) { model_name.to_s }
15
+
16
+ #let(:model_plural) { model_name.plural }
17
+ let(:model_plural) { model_singular.to_s.pluralize.to_sym }
18
+
19
+ let(:model_class) { new_model.class }
20
+
21
+ let(:parent_name) { nil }
22
+
23
+ let(:parent) { parent_name and new_model.send(parent_name) }
24
+
25
+ #let(:foster_parent) { parent_name and create(parent_name) }
26
+
27
+ # Rest paths
28
+
29
+ #let :foster_parent_path do
30
+ # foster_parent ? "/#{foster_parent.model_name.plural}/#{foster_parent.id}" : ''
31
+ #end
32
+
33
+ let(:scope) { nil }
34
+
35
+ let(:controller_name) { model_plural }
36
+
37
+ let(:scoped) { scope ? "#{scope}/" : '' }
38
+
39
+ let(:parent_elements) { parent ? "/#{parent.model_name.plural}/#{parent.id}" : '' }
40
+
41
+ let(:collection_path) { "#{scoped}#{parent_elements}/#{controller_name}" }
42
+
43
+ let(:member_path) { "#{collection_path}/#{model.id}" }
44
+
45
+ # Model sample field names and values
46
+
47
+ let(:field_name) { :name }
48
+
49
+ let(:field_value) { 'Previous' }
50
+
51
+ let(:new_field_value) { 'Latest' }
52
+
53
+ let(:invalid_field_value) { '' }
54
+
55
+ let(:invalid_text) { 'blank' }
56
+
57
+ let(:field_values) { %w(First Second Third) }
58
+
59
+ # Model instances
60
+
61
+ let(:new_model) { build model_singular, field_name => field_value }
62
+
63
+ let(:model) { create model_singular, field_name => field_value }
64
+
65
+ let(:models) do
66
+ field_values.map {|name| create model_singular, field_name => name }
67
+ end
68
+
69
+ # Request parameters
70
+
71
+ def model_params(**params)
72
+ { model_singular => params }
73
+ end
74
+
75
+ let(:format) { { format: :json } }
76
+
77
+ # Utilities
78
+
79
+ def json(response)
80
+ JSON.parse response.body
81
+ end
82
+
83
+ def extract(response, key=nil)
84
+ parsed = json response
85
+
86
+ if block_given?
87
+ yield parsed
88
+ else
89
+ parsed[key.to_s]
90
+ end
91
+ end
92
+
93
+ def find_model(value=new_field_value)
94
+ model_class.find_by(field_name => value)
95
+ end
96
+
97
+ if not actions or actions.include? :index
98
+ describe "GET #index" do
99
+ it "shows models" do
100
+ models
101
+
102
+ response = get(collection_path, format)
103
+
104
+ expect(response.status).to eq STATUS_SUCCESS
105
+
106
+ values = extract(response) do |atts_array|
107
+ atts_array.map {|atts| atts[field_name.to_s] }
108
+ end
109
+
110
+ expect(values).to eq field_values
111
+ end
112
+ end
113
+ end
114
+
115
+ if not actions or actions.include? :show
116
+ describe "GET #show" do
117
+ it "shows model" do
118
+ response = get(member_path, format)
119
+
120
+ expect(response.status).to eq STATUS_SUCCESS
121
+
122
+ expect(extract response, field_name).to eq field_value
123
+ end
124
+ end
125
+ end
126
+
127
+ if not actions or actions.include? :create
128
+ describe "POST #create" do
129
+ it "saves valid model" do
130
+ params = model_params(field_name => new_field_value).merge format
131
+
132
+ response = post(collection_path, params)
133
+
134
+ expect(response.status).to eq STATUS_CREATED
135
+
136
+ expect(extract response, field_name).to eq new_field_value
137
+
138
+ expect(find_model[field_name]).to eq new_field_value
139
+ end
140
+
141
+ it "handles invalid model" do
142
+ params = model_params(field_name => invalid_field_value).merge format
143
+
144
+ response = post(collection_path, params)
145
+
146
+ expect(response.status).to eq UNPROCESSABLE_ENTITY
147
+
148
+ expect(extract(response, field_name).first).to match /#{invalid_text}/
149
+ end
150
+ end
151
+ end
152
+
153
+ if not actions or actions.include? :update
154
+ describe "PUT #update" do
155
+ it "alters valid model" do
156
+ params = model_params(field_name => new_field_value).merge format
157
+
158
+ response = put(member_path, params)
159
+
160
+ expect(response.status).to eq STATUS_SUCCESS
161
+
162
+ expect(extract response, field_name).to eq new_field_value
163
+
164
+ expect(find_model[field_name]).to eq new_field_value
165
+ end
166
+
167
+ it "handles invalid model" do
168
+ params = model_params(field_name => invalid_field_value).merge format
169
+
170
+ response = put(member_path, params)
171
+
172
+ expect(response.status).to eq UNPROCESSABLE_ENTITY
173
+
174
+ expect(extract(response, field_name).first).to match /#{invalid_text}/
175
+ end
176
+ end
177
+ end
178
+
179
+ if not actions or actions.include?(:delete) or actions.include?(:destroy)
180
+ describe "DELETE #destroy" do
181
+ it "deletes model" do
182
+ model
183
+
184
+ expect { delete(member_path, format) }
185
+ .to change { model_class.count }
186
+ .by(-1)
187
+ end
188
+
189
+ it "returns no_content status" do
190
+ response = delete(member_path, format)
191
+
192
+ expect(response.status).to eq STATUS_NO_CONTENT
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,36 @@
1
+
2
+ module ModelParentHelpers
3
+ def self.included(base)
4
+ base.class_eval do
5
+ # TODO: change!
6
+ let(:scope) { nil }
7
+
8
+ # TODO: change!
9
+ let(:new_model) { build :answer }
10
+
11
+ # TODO: change!
12
+ let(:model) { create :answer }
13
+
14
+ # TODO: change!
15
+ let(:parent) { model.question }
16
+
17
+ # TODO: change!
18
+ let(:grand_parent) { parent.category }
19
+
20
+ # The following can be left alone
21
+
22
+ let(:model_plural) { model.model_name.plural }
23
+
24
+ let(:parent_plural) { parent.model_name.plural }
25
+
26
+ let(:grand_parent_singular) { grand_parent.model_name.singular }
27
+
28
+ let(:grand_parent_plural) { grand_parent.model_name.plural }
29
+ end
30
+ end
31
+
32
+ def path_re(prefix: '', suffix: '', parent: parent_plural, model: model_plural)
33
+ %r(^#{prefix}#{scope and '/' + scope}/#{parent}/\d+/#{model}#{suffix}$)
34
+ end
35
+ end
36
+
@@ -0,0 +1,43 @@
1
+
2
+ require 'active_support/concern'
3
+
4
+ module RestLinkProcsAssignments
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ #let(:renderer) { Templet::Renderer.new(self) }
9
+ let :renderer do
10
+ Class.new do
11
+ def call(&block)
12
+ instance_eval &block
13
+ end
14
+
15
+ def link_to(*args)
16
+ args
17
+ end
18
+ end.new
19
+ end
20
+
21
+ let(:child) { build :answer }
22
+ let(:model) { child.question }
23
+ let(:parent) { model.category }
24
+
25
+ let(:model_singular) { model.model_name.singular.to_sym }
26
+ let(:model_plural) { model.model_name.plural.to_sym }
27
+
28
+ let(:parent_plural) { parent.model_name.plural.to_sym }
29
+ let(:parent_singular) { parent.model_name.singular.to_sym }
30
+
31
+ let (:single_path) { [ model ] }
32
+ let (:dual_path) { [ parent, model ] }
33
+
34
+ let(:param) { { q: 'jumper' } }
35
+
36
+ let(:default_param) { { token: 'book' } }
37
+
38
+ let(:param_options) { { default_params: default_param } }
39
+
40
+ let(:remote_options) { { remote: true } }
41
+ end
42
+ end
43
+
@@ -0,0 +1,15 @@
1
+
2
+ module RestLinkProcsHelpers
3
+ def link_procs(**options)
4
+ options.reverse_merge! verify_path: false
5
+
6
+ Templet::Links::RestLinkProcs.new(nil, **options)
7
+ end
8
+
9
+ def execute(link_proc, model, parent, **opts)
10
+ link = link_proc.(renderer, model, parent)
11
+
12
+ return link, link[1]
13
+ end
14
+ end
15
+
@@ -0,0 +1,32 @@
1
+
2
+ require "nokogiri"
3
+
4
+ module PartialTestHelpers
5
+ def renderer
6
+ Templet::Renderer.new helper
7
+ end
8
+
9
+ def partial_for(klass, *args)
10
+ klass.new(renderer, *args)
11
+ end
12
+ def partial(*args, **options)
13
+ partial_for(described_class, *args, **options)
14
+ end
15
+
16
+ def render_partial(*args, &block)
17
+ partial.(*args, &block)
18
+ end
19
+
20
+ def parse(html)
21
+ Nokogiri::HTML(html)
22
+ end
23
+
24
+ def parse_html(selector, single_return: true,
25
+ render_args: [],
26
+ html: render_partial(*render_args))
27
+ method = single_return ? :at_css : :css
28
+
29
+ parse(html).send(method, selector)
30
+ end
31
+ end
32
+
@@ -0,0 +1,105 @@
1
+
2
+ #require_relative "partial_test_helpers"
3
+
4
+ shared_examples 'a viewer' do |actions|
5
+ include PartialTestHelpers
6
+
7
+ let(:scope) { nil }
8
+
9
+ let(:controller_name) { nil }
10
+
11
+ let(:model) { create model_name, field_name => field_value }
12
+
13
+ let(:parent) { model.send parent_name if model.respond_to?(parent_name || '') }
14
+
15
+ let(:new_model) { build model_name, field_name => field_value }
16
+
17
+ let(:field_values) { %w(First Second Third) }
18
+
19
+ let :models do
20
+ field_values.map {|value| create model_name, field_name => value }
21
+ end
22
+
23
+ let(:parent_name) { }
24
+
25
+ let(:grand_parent_name) { }
26
+
27
+ let(:field_name) { :name }
28
+
29
+ let(:field_value) { "#{model_name} #{field_name}" }
30
+
31
+ let(:input_field_tag) { "input" }
32
+
33
+ let(:index_selector) { 'tbody tr td:first' }
34
+
35
+ let(:field_selector) { "#{input_field_tag}[id=#{model_name}_#{field_name}]" }
36
+
37
+ let(:list_selector) { 'dl dd:first' }
38
+
39
+ def viewer(model, parent=nil, **opts)
40
+ if scope or controller_name
41
+ opts[:controller] = (scope ? "#{scope}/" : '') +
42
+ (controller_name&.to_s || model_name.to_s.pluralize)
43
+ end
44
+
45
+ opts[:grand_parent] = grand_parent_name if grand_parent_name
46
+
47
+ described_class.new helper, model, parent, **opts
48
+ end
49
+
50
+ if not actions or actions.include? :index
51
+ describe '#index' do
52
+ it "shows model queries" do
53
+ html = viewer(model_name, parent, models: models).index
54
+
55
+ queries = parse_html(index_selector, html: html, single_return: false)
56
+
57
+ expect(queries.map(&:text)).to eq field_values
58
+ end
59
+ end
60
+ end
61
+
62
+ if not actions or actions.include? :show
63
+ describe '#show' do
64
+ it 'displays field value' do
65
+ html = viewer(model, parent).show
66
+
67
+ value = parse_html(list_selector, html: html).text
68
+
69
+ expect(value).to eq field_value
70
+ end
71
+ end
72
+ end
73
+
74
+ if not actions or actions.include? :new
75
+ describe '#new' do
76
+ it 'prompts for field value' do
77
+ helper.instance_variable_set("@#{model_name}", new_model)
78
+
79
+ html = viewer(new_model, parent).new
80
+
81
+ selected = parse_html(field_selector, html: html)
82
+
83
+ value = selected['value'] || selected.text
84
+
85
+ expect(value.strip).to eq field_value
86
+ end
87
+ end
88
+ end
89
+
90
+ if not actions or actions.include? :edit
91
+ describe '#edit' do
92
+ it 'prompts for field value' do
93
+ helper.instance_variable_set("@#{model_name}", model)
94
+
95
+ html = viewer(model, parent).edit
96
+
97
+ selected = parse_html(field_selector, html: html)
98
+
99
+ value = selected['value'] || selected.text
100
+
101
+ expect(value.strip).to eq field_value
102
+ end
103
+ end
104
+ end
105
+ end