abyme 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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