abyme 0.5.1 → 0.6.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.
data/lib/abyme/version.rb CHANGED
@@ -2,8 +2,8 @@
2
2
  module Abyme
3
3
  module VERSION
4
4
  MAJOR = 0
5
- MINOR = 5
6
- PATCH = 1
5
+ MINOR = 6
6
+ PATCH = 0
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH].join(".")
9
9
  end
@@ -2,7 +2,6 @@ require_relative "abyme_builder"
2
2
 
3
3
  module Abyme
4
4
  module ViewHelpers
5
-
6
5
  # ABYME_FOR
7
6
 
8
7
  # this helper will generate the top level wrapper markup
@@ -23,7 +22,7 @@ module Abyme
23
22
  # set the default number of blank fields to display
24
23
 
25
24
  # - partial (String)
26
- # to customize the partial path by default #abyme_for will expect
25
+ # to customize the partial path by default #abyme_for will expect
27
26
  # a partial to bbe present in views/abyme
28
27
 
29
28
  # - Exemple
@@ -39,22 +38,24 @@ module Abyme
39
38
  # </div>
40
39
 
41
40
  def abyme_for(association, form, options = {}, &block)
42
- content_tag(:div, data: { controller: 'abyme', limit: options[:limit], min_count: options[:min_count] }, id: "abyme--#{association}") do
43
- if block_given?
44
- yield(Abyme::AbymeBuilder.new(
45
- association: association, form: form, context: self, partial: options[:partial]
41
+ content_tag(:div, data: {controller: "abyme", limit: options[:limit], min_count: options[:min_count]}, id: "abyme--#{association}") do
42
+ if block
43
+ yield(
44
+ Abyme::AbymeBuilder.new(
45
+ association: association, form: form, context: self, partial: options[:partial]
46
46
  )
47
47
  )
48
48
  else
49
- model = association.to_s.singularize.classify.constantize
49
+ # model = association.to_s.singularize.classify.constantize
50
+ model = association.to_s.singularize
50
51
  concat(persisted_records_for(association, form, options))
51
- concat(new_records_for(association, form, options))
52
+ concat(new_records_for(association, form, options))
52
53
  concat(add_associated_record(content: options[:button_text] || "Add #{model}"))
53
54
  end
54
55
  end
55
56
  end
56
57
 
57
- alias :abymize :abyme_for
58
+ alias_method :abymize, :abyme_for
58
59
 
59
60
  # NEW_RECORDS_FOR
60
61
 
@@ -72,9 +73,9 @@ module Abyme
72
73
  # will output this html
73
74
 
74
75
  # <div data-target="abyme.associations" data-association="tasks" data-abyme-position="end">
75
- # <template class="abyme--task_template" data-target="abyme.template">
76
+ # <template class="abyme--task_template" data-target="abyme.template">
76
77
  # <div data-target="abyme.fields abyme.newFields" class="abyme--fields task-fields">
77
- # ... partial html goes here
78
+ # ... partial html goes here
78
79
  # </div>
79
80
  # </template>
80
81
  # ... new rendered fields goes here
@@ -82,41 +83,41 @@ module Abyme
82
83
 
83
84
  # == Options
84
85
  # - position (:start, :end)
85
- # allows you to specify whether new fields added dynamically
86
- # should go at the top or at the bottom
86
+ # allows you to specify whether new fields added dynamically
87
+ # should go at the top or at the bottom
87
88
  # :end is the default value
88
89
 
89
90
  # - partial (String)
90
- # to customize the partial path by default #abyme_for will expect
91
+ # to customize the partial path by default #abyme_for will expect
91
92
  # a partial to bbe present in views/abyme
92
93
 
93
94
  # - fields_html (Hash)
94
95
  # allows you to pass any html attributes to each fields wrapper
95
96
 
96
97
  # - wrapper_html (Hash)
97
- # allows you to pass any html attributes to the the html element
98
+ # allows you to pass any html attributes to the the html element
98
99
  # wrapping all the fields
99
100
 
100
101
  def new_records_for(association, form, options = {}, &block)
101
102
  options[:wrapper_html] ||= {}
102
103
 
103
- wrapper_default = {
104
- data: {
105
- abyme_target: 'associations',
106
- association: association,
107
- abyme_position: options[:position] || :end
108
- }
104
+ wrapper_default = {
105
+ data: {
106
+ abyme_target: "associations",
107
+ association: association,
108
+ abyme_position: options[:position] || :end
109
+ }
109
110
  }
110
111
 
111
- fields_default = { data: { target: 'abyme.fields abyme.newFields' } }
112
+ fields_default = {data: {target: "abyme.fields abyme.newFields"}}
112
113
 
113
114
  content_tag(:div, build_attributes(wrapper_default, options[:wrapper_html])) do
114
- content_tag(:template, class: "abyme--#{association.to_s.singularize}_template", data: { abyme_target: 'template' }) do
115
- form.fields_for association, association.to_s.classify.constantize.new, child_index: 'NEW_RECORD' do |f|
115
+ content_tag(:template, class: "abyme--#{association.to_s.singularize}_template", data: {abyme_target: "template"}) do
116
+ form.fields_for association, association.to_s.classify.constantize.new, child_index: "NEW_RECORD" do |f|
116
117
  content_tag(:div, build_attributes(fields_default, basic_fields_markup(options[:fields_html], association))) do
117
118
  # Here, if a block is passed, we're passing the association fields to it, rather than the form itself
118
119
  # block_given? ? yield(f) : render(options[:partial] || "abyme/#{association.to_s.singularize}_fields", f: f)
119
- block_given? ? yield(f) : render_association_partial(association, f, options[:partial])
120
+ block ? yield(f) : render_association_partial(association, f, options[:partial])
120
121
  end
121
122
  end
122
123
  end
@@ -154,21 +155,23 @@ module Abyme
154
155
  # ex: order: { created_at: :desc }
155
156
 
156
157
  # - partial (String)
157
- # to customize the partial path by default #abyme_for will expect
158
+ # to customize the partial path by default #abyme_for will expect
158
159
  # a partial to bbe present in views/abyme
159
160
 
160
161
  # - fields_html (Hash)
161
162
  # allows you to pass any html attributes to each fields wrapper
162
163
 
163
164
  # - wrapper_html (Hash)
164
- # allows you to pass any html attributes to the the html element
165
+ # allows you to pass any html attributes to the the html element
165
166
  # wrapping all the fields
166
-
167
+
167
168
  def persisted_records_for(association, form, options = {})
168
169
  records = options[:collection] || form.object.send(association)
170
+ # return if records.empty?
171
+
169
172
  options[:wrapper_html] ||= {}
170
- fields_default = { data: { abyme_target: 'fields' } }
171
-
173
+ fields_default = {data: {abyme_target: "fields"}}
174
+
172
175
  if options[:order].present?
173
176
  records = records.order(options[:order])
174
177
  # by calling the order method on the AR collection
@@ -176,8 +179,8 @@ module Abyme
176
179
  # so we have to get them back with the 2 lines below
177
180
  invalids = form.object.send(association).reject(&:persisted?)
178
181
  records = records.to_a.concat(invalids) if invalids.any?
179
- end
180
-
182
+ end
183
+
181
184
  content_tag(:div, options[:wrapper_html]) do
182
185
  form.fields_for(association, records) do |f|
183
186
  content_tag(:div, build_attributes(fields_default, basic_fields_markup(options[:fields_html], association))) do
@@ -189,24 +192,24 @@ module Abyme
189
192
 
190
193
  # ADD & REMOVE ASSOCIATION
191
194
 
192
- # these helpers will call the #create_button method
195
+ # these helpers will call the #create_button method
193
196
  # to generate the buttons for add and remove associations
194
197
  # with the right action and a default content text for each button
195
-
198
+
196
199
  def add_associated_record(options = {}, &block)
197
- action = 'click->abyme#add_association'
198
- options[:content] ||= 'Add Association'
200
+ action = "click->abyme#add_association"
201
+ options[:content] ||= "Add Association"
199
202
  create_button(action, options, &block)
200
203
  end
201
-
204
+
202
205
  def remove_associated_record(options = {}, &block)
203
- action = 'click->abyme#remove_association'
204
- options[:content] ||= 'Remove Association'
206
+ action = "click->abyme#remove_association"
207
+ options[:content] ||= "Remove Association"
205
208
  create_button(action, options, &block)
206
209
  end
207
210
 
208
- alias :add_association :add_associated_record
209
- alias :remove_association :remove_associated_record
211
+ alias_method :add_association, :add_associated_record
212
+ alias_method :remove_association, :remove_associated_record
210
213
 
211
214
  private
212
215
 
@@ -225,17 +228,17 @@ module Abyme
225
228
 
226
229
  # - html (Hash)
227
230
  # to pass any html attributes you want.
228
-
231
+
229
232
  def create_button(action, options, &block)
230
233
  options[:html] ||= {}
231
234
  options[:tag] ||= :button
232
-
233
- if block_given?
234
- content_tag(options[:tag], { data: { action: action } }.merge(options[:html])) do
235
+
236
+ if block
237
+ content_tag(options[:tag], {data: {action: action}}.merge(options[:html])) do
235
238
  capture(&block)
236
239
  end
237
240
  else
238
- content_tag(options[:tag], options[:content], { data: { action: action } }.merge(options[:html]))
241
+ content_tag(options[:tag], options[:content], {data: {action: action}}.merge(options[:html]))
239
242
  end
240
243
  end
241
244
 
@@ -246,7 +249,7 @@ module Abyme
246
249
 
247
250
  def basic_fields_markup(html, association = nil)
248
251
  if html && html[:class]
249
- html[:class] = "abyme--fields #{association.to_s.singularize}-fields #{html[:class]}"
252
+ html[:class] = "abyme--fields #{association.to_s.singularize}-fields #{html[:class]}"
250
253
  else
251
254
  html ||= {}
252
255
  html[:class] = "abyme--fields #{association.to_s.singularize}-fields"
@@ -274,13 +277,12 @@ module Abyme
274
277
 
275
278
  # RENDER PARTIAL
276
279
 
277
- # renders a partial based on the passed path, or will expect a partial to be found in the views/abyme directory.
280
+ # renders a partial based on the passed path, or will expect a partial to be found in the views/abyme directory.
278
281
 
279
282
  def render_association_partial(association, form, partial = nil, context = nil)
280
- partial_path = partial ||"abyme/#{association.to_s.singularize}_fields"
283
+ partial_path = partial || "abyme/#{association.to_s.singularize}_fields"
281
284
  context ||= self
282
285
  context.render(partial: partial_path, locals: {f: form})
283
286
  end
284
-
285
287
  end
286
- end
288
+ end
@@ -0,0 +1,16 @@
1
+ Description:
2
+ Injects the call to the `abyme_attributes` in the strong params definition of the targeted controller.
3
+ Also works with namespaced controllers.
4
+
5
+ Example:
6
+ Before :
7
+ def project_params
8
+ params.require(:project).permit(:title, :description)
9
+ end
10
+
11
+ rails generate abyme:controller Projects
12
+
13
+ After:
14
+ def project_params
15
+ params.require(:project).permit(abyme_attributes, :title, :description)
16
+ end
@@ -0,0 +1,25 @@
1
+ require 'rails/generators'
2
+
3
+ module Abyme
4
+ module Generators
5
+ class ControllerGenerator < Rails::Generators::NamedBase
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ def insert_abyme_attributes_in_strong_params
9
+ return unless File.exists? controller_file_path
10
+
11
+ insert_into_file(
12
+ controller_file_path,
13
+ "abyme_attributes, ",
14
+ after: /^(\w|\s)*params\s*(.*)permit\(\s*/
15
+ )
16
+ end
17
+
18
+ private
19
+
20
+ def controller_file_path
21
+ Rails.root.join('app', 'controllers', "#{name.downcase.pluralize}_controller.rb")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ Description:
2
+ Generates configuration for the model part :
3
+ ✅ Adds the `includes Abyme::Model` at the top
4
+ ✅ Will add the call to `abymize` below the targeted association, including the optional permitted attributes
5
+
6
+ 💡 Works with namespaced models
7
+
8
+ Example:
9
+ rails generate abyme:model project tasks description title
10
+ rails generate abyme:model project participants all
11
+
12
+ In project.rb, you will find :
13
+ class Project < ApplicationRecord
14
+ include Abyme::Model
15
+
16
+ has_many :tasks
17
+ abymize :tasks, permit: [:description, :title]
18
+
19
+ has_many :participants
20
+ abymize :participants, permit: :all_attributes
21
+ end
@@ -0,0 +1,70 @@
1
+ require 'rails/generators'
2
+
3
+ module Abyme
4
+ module Generators
5
+ class ModelGenerator < Rails::Generators::NamedBase
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ argument :association, type: :string, required: true, banner: "association association"
9
+ argument :attributes, type: :array, default: [], banner: "field field"
10
+
11
+ def insert_abyme_config_in_model
12
+ insert_abyme_configuration unless model_configured?
13
+ insert_abymized_association
14
+ end
15
+
16
+ private
17
+
18
+ def insert_abymized_association
19
+ insert_into_file(model_file_path, after: /(^\s*(has_many|has_one|belongs_to)\s*:#{Regexp.quote(association)}.*$)/ ) do
20
+ "\n#{insert_indentation}abymize :#{association}#{inject_abyme_attributes}\n"
21
+ end
22
+ end
23
+
24
+ def insert_abyme_configuration
25
+ if namespaced_model
26
+ model = namespaced_model[2]
27
+ namespace = namespaced_model[1]
28
+ insert_into_file(model_file_path, after: /(^\s*class.*#{Regexp.quote(model)}.*$)/) do
29
+ "\n include Abyme::Model\n"
30
+ end
31
+ else
32
+ inject_into_class(model_file_path, class_name, " include Abyme::Model\n\n")
33
+ end
34
+ end
35
+
36
+ def assign_names!(name)
37
+ # Remove abyme namespace
38
+ name.gsub!(/abyme_/, "")
39
+ super
40
+ end
41
+
42
+ def namespaced_model
43
+ class_name.match(/(.*)[\/::](.*)/)
44
+ end
45
+
46
+ def model_file_path
47
+ Rails.root.join('app', 'models', "#{name}.rb")
48
+ end
49
+
50
+ def inject_abyme_attributes
51
+ return '' if attributes.empty?
52
+ return ", permit: :all_attributes" if attributes.map(&:name).include?('all_attributes')
53
+
54
+ ", permit: [#{symbolized_attributes.join(', ')}]"
55
+ end
56
+
57
+ def symbolized_attributes
58
+ attributes.map {|attr| ":#{attr.name.downcase}" }
59
+ end
60
+
61
+ def model_configured?
62
+ File.read(model_file_path).match?(/Abyme::Model/)
63
+ end
64
+
65
+ def insert_indentation
66
+ namespaced_model ? " " : " "
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,10 @@
1
+ Description:
2
+ Generates configuration for the model and controller, as well as views templates (see each individual usage pages)
3
+
4
+ Example:
5
+ # Generate files and configuration, not handling attributes
6
+ rails generate abyme:model project tasks
7
+ # Generate files and configuration, permitting specified attributes
8
+ rails generate abyme:model project tasks description title
9
+ # Generate files and configuration, permitting all attributes
10
+ rails generate abyme:model project tasks all_attributes
@@ -0,0 +1,18 @@
1
+ require 'rails/generators'
2
+
3
+ module Abyme
4
+ module Generators
5
+ class ResourceGenerator < Rails::Generators::NamedBase
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ argument :association, type: :string, required: true, banner: "association association"
9
+ argument :attributes, type: :array, default: [], banner: "field field"
10
+
11
+ def call_generators
12
+ Rails::Generators.invoke "abyme:model", [name, association, *attributes.map(&:name)]
13
+ Rails::Generators.invoke "abyme:controller", [name]
14
+ Rails::Generators.invoke "abyme:view", [association, *attributes.map(&:name)]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Registers the Abyme stimulus controller into the javascript/controllers/index.js
3
+
4
+ Example:
5
+ rails generate abyme:stimulus
@@ -0,0 +1,21 @@
1
+ require 'rails/generators'
2
+
3
+ module Abyme
4
+ module Generators
5
+ class StimulusGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ def add_to_stimulus
9
+ insert_into_file(stimulus_file_path, "\nimport { AbymeController } from 'abyme';\n")
10
+ insert_into_file(stimulus_file_path, "application.register('abyme', AbymeController);")
11
+ end
12
+
13
+ private
14
+
15
+ def stimulus_file_path
16
+ Rails.root.join('app', 'javascript', 'controllers', 'index.js')
17
+ end
18
+
19
+ end
20
+ end
21
+ end