blueprinter-rb 1.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +167 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1050 -0
- data/Rakefile +30 -0
- data/lib/blueprinter/base.rb +458 -0
- data/lib/blueprinter/blueprinter_error.rb +5 -0
- data/lib/blueprinter/configuration.rb +39 -0
- data/lib/blueprinter/deprecation.rb +37 -0
- data/lib/blueprinter/empty_types.rb +32 -0
- data/lib/blueprinter/extractor.rb +13 -0
- data/lib/blueprinter/extractors/association_extractor.rb +33 -0
- data/lib/blueprinter/extractors/auto_extractor.rb +37 -0
- data/lib/blueprinter/extractors/block_extractor.rb +10 -0
- data/lib/blueprinter/extractors/hash_extractor.rb +10 -0
- data/lib/blueprinter/extractors/public_send_extractor.rb +10 -0
- data/lib/blueprinter/field.rb +65 -0
- data/lib/blueprinter/formatters/date_time_formatter.rb +33 -0
- data/lib/blueprinter/helpers/base_helpers.rb +123 -0
- data/lib/blueprinter/helpers/type_helpers.rb +15 -0
- data/lib/blueprinter/transformer.rb +14 -0
- data/lib/blueprinter/version.rb +5 -0
- data/lib/blueprinter/view.rb +78 -0
- data/lib/blueprinter/view_collection.rb +90 -0
- data/lib/blueprinter.rb +6 -0
- data/lib/generators/blueprinter/blueprint_generator.rb +129 -0
- data/lib/generators/blueprinter/templates/blueprint.rb +16 -0
- metadata +214 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
class Blueprinter::Field
|
5
|
+
attr_reader :method, :name, :extractor, :options, :blueprint
|
6
|
+
def initialize(method, name, extractor, blueprint, options = {})
|
7
|
+
@method = method
|
8
|
+
@name = name
|
9
|
+
@extractor = extractor
|
10
|
+
@blueprint = blueprint
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def extract(object, local_options)
|
15
|
+
extractor.extract(method, object, local_options, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def skip?(field_name, object, local_options)
|
19
|
+
return true if if_callable && !if_callable.call(field_name, object, local_options)
|
20
|
+
unless_callable && unless_callable.call(field_name, object, local_options)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def if_callable
|
26
|
+
return @if_callable if defined?(@if_callable)
|
27
|
+
@if_callable = callable_from(:if)
|
28
|
+
end
|
29
|
+
|
30
|
+
def unless_callable
|
31
|
+
return @unless_callable if defined?(@unless_callable)
|
32
|
+
@unless_callable = callable_from(:unless)
|
33
|
+
end
|
34
|
+
|
35
|
+
def callable_from(condition)
|
36
|
+
callable = old_callable_from(condition)
|
37
|
+
|
38
|
+
if callable && callable.arity == 2
|
39
|
+
Blueprinter::Deprecation.report("`:#{condition}` conditions now expects 3 arguments instead of 2.")
|
40
|
+
->(_field_name, obj, options) { callable.call(obj, options) }
|
41
|
+
else
|
42
|
+
callable
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def old_callable_from(condition)
|
47
|
+
config = Blueprinter.configuration
|
48
|
+
|
49
|
+
# Use field-level callable, or when not defined, try global callable
|
50
|
+
tmp = if options.key?(condition)
|
51
|
+
options.fetch(condition)
|
52
|
+
elsif config.valid_callable?(condition)
|
53
|
+
config.public_send(condition)
|
54
|
+
end
|
55
|
+
|
56
|
+
return false unless tmp
|
57
|
+
|
58
|
+
case tmp
|
59
|
+
when Proc then tmp
|
60
|
+
when Symbol then blueprint.method(tmp)
|
61
|
+
else
|
62
|
+
raise ArgumentError, "#{tmp.class} is passed to :#{condition}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Blueprinter
|
4
|
+
class DateTimeFormatter
|
5
|
+
InvalidDateTimeFormatterError = Class.new(BlueprinterError)
|
6
|
+
|
7
|
+
def format(value, options)
|
8
|
+
return value if value.nil?
|
9
|
+
|
10
|
+
field_format = options[:datetime_format]
|
11
|
+
if value.respond_to?(:strftime)
|
12
|
+
value = format_datetime(value, field_format)
|
13
|
+
elsif field_format
|
14
|
+
raise InvalidDateTimeFormatterError, 'Cannot format invalid DateTime object'
|
15
|
+
end
|
16
|
+
value
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def format_datetime(value, field_format)
|
22
|
+
format = field_format || Blueprinter.configuration.datetime_format
|
23
|
+
|
24
|
+
case format
|
25
|
+
when NilClass then value
|
26
|
+
when Proc then format.call(value)
|
27
|
+
when String then value.strftime(format)
|
28
|
+
else
|
29
|
+
raise InvalidDateTimeFormatterError, 'Cannot format DateTime object with invalid formatter: #{format.class}'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Blueprinter
|
4
|
+
module BaseHelpers
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(SingletonMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module SingletonMethods
|
10
|
+
include TypeHelpers
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def prepare_for_render(object, options)
|
15
|
+
view_name = options.delete(:view) || :default
|
16
|
+
root = options.delete(:root)
|
17
|
+
meta = options.delete(:meta)
|
18
|
+
validate_root_and_meta!(root, meta)
|
19
|
+
prepare(object, view_name: view_name, local_options: options, root: root, meta: meta)
|
20
|
+
end
|
21
|
+
|
22
|
+
def prepare_data(object, view_name, local_options)
|
23
|
+
if array_like?(object)
|
24
|
+
object.map do |obj|
|
25
|
+
object_to_hash(obj,
|
26
|
+
view_name: view_name,
|
27
|
+
local_options: local_options)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
object_to_hash(object,
|
31
|
+
view_name: view_name,
|
32
|
+
local_options: local_options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def prepend_root_and_meta(data, root, meta)
|
37
|
+
return data unless root
|
38
|
+
ret = {root => data}
|
39
|
+
meta ? ret.merge!(meta: meta) : ret
|
40
|
+
end
|
41
|
+
|
42
|
+
def inherited(subclass)
|
43
|
+
subclass.send(:view_collection).inherit(view_collection)
|
44
|
+
end
|
45
|
+
|
46
|
+
def object_to_hash(object, view_name:, local_options:)
|
47
|
+
result_hash = view_collection.fields_for(view_name).each_with_object({}) do |field, hash|
|
48
|
+
next if field.skip?(field.name, object, local_options)
|
49
|
+
hash[field.name] = field.extract(object, local_options)
|
50
|
+
end
|
51
|
+
view_collection.transformers(view_name).each do |transformer|
|
52
|
+
transformer.transform(result_hash, object, local_options)
|
53
|
+
end
|
54
|
+
result_hash
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate_root_and_meta!(root, meta)
|
58
|
+
case root
|
59
|
+
when String, Symbol
|
60
|
+
# no-op
|
61
|
+
when NilClass
|
62
|
+
raise BlueprinterError, "meta requires a root to be passed" if meta
|
63
|
+
else
|
64
|
+
raise BlueprinterError, "root should be one of String, Symbol, NilClass"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def dynamic_blueprint?(blueprint)
|
69
|
+
blueprint.is_a?(Proc)
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate_blueprint!(blueprint, method)
|
73
|
+
validate_presence_of_blueprint!(blueprint)
|
74
|
+
unless dynamic_blueprint?(blueprint)
|
75
|
+
validate_blueprint_has_ancestors!(blueprint, method)
|
76
|
+
validate_blueprint_has_blueprinter_base_ancestor!(blueprint, method)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def validate_presence_of_blueprint!(blueprint)
|
81
|
+
raise BlueprinterError, 'Blueprint required' unless blueprint
|
82
|
+
end
|
83
|
+
|
84
|
+
def validate_blueprint_has_ancestors!(blueprint, association_name)
|
85
|
+
# If the class passed as a blueprint does not respond to ancestors
|
86
|
+
# it means it, at the very least, does not have Blueprinter::Base as
|
87
|
+
# one of its ancestor classes (e.g: Hash) and thus an error should
|
88
|
+
# be raised.
|
89
|
+
unless blueprint.respond_to?(:ancestors)
|
90
|
+
raise BlueprinterError, "Blueprint provided for #{association_name} "\
|
91
|
+
'association is not valid.'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def validate_blueprint_has_blueprinter_base_ancestor!(blueprint, association_name)
|
96
|
+
# Guard clause in case Blueprinter::Base is present in the ancestor list
|
97
|
+
# for the blueprint class provided.
|
98
|
+
return if blueprint.ancestors.include? Blueprinter::Base
|
99
|
+
|
100
|
+
# Raise error describing what's wrong.
|
101
|
+
raise BlueprinterError, "Class #{blueprint.name} does not inherit from "\
|
102
|
+
'Blueprinter::Base and is not a valid Blueprinter '\
|
103
|
+
"for #{association_name} association."
|
104
|
+
end
|
105
|
+
|
106
|
+
def jsonify(blob)
|
107
|
+
Blueprinter.configuration.jsonify(blob)
|
108
|
+
end
|
109
|
+
|
110
|
+
def current_view
|
111
|
+
@current_view ||= view_collection[:default]
|
112
|
+
end
|
113
|
+
|
114
|
+
def view_collection
|
115
|
+
@view_collection ||= ViewCollection.new
|
116
|
+
end
|
117
|
+
|
118
|
+
def associations(view_name = :default)
|
119
|
+
view_collection.fields_for(view_name).select { |f| f.options[:association] }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Blueprinter
|
4
|
+
module TypeHelpers
|
5
|
+
private
|
6
|
+
def active_record_relation?(object)
|
7
|
+
!!(defined?(ActiveRecord::Relation) &&
|
8
|
+
object.is_a?(ActiveRecord::Relation))
|
9
|
+
end
|
10
|
+
|
11
|
+
def array_like?(object)
|
12
|
+
object.is_a?(Array) || active_record_relation?(object)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Blueprinter
|
4
|
+
# @api private
|
5
|
+
class Transformer
|
6
|
+
def transform(_result_hash, _primary_obj, _options = {})
|
7
|
+
fail NotImplementedError, "A Transformer must implement #transform"
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.transform(result_hash, primary_obj, options = {})
|
11
|
+
self.new.transform(result_hash, primary_obj, options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Blueprinter
|
4
|
+
# @api private
|
5
|
+
DefinitionPlaceholder = Struct.new :name, :view?
|
6
|
+
class View
|
7
|
+
attr_reader :excluded_field_names, :fields, :included_view_names, :name, :view_transformers, :definition_order
|
8
|
+
|
9
|
+
def initialize(name, fields: {}, included_view_names: [], excluded_view_names: [], transformers: [])
|
10
|
+
@name = name
|
11
|
+
@fields = fields
|
12
|
+
@included_view_names = included_view_names
|
13
|
+
@excluded_field_names = excluded_view_names
|
14
|
+
@view_transformers = transformers
|
15
|
+
@definition_order = []
|
16
|
+
@sort_by_definition = Blueprinter.configuration.sort_fields_by.eql?(:definition)
|
17
|
+
end
|
18
|
+
|
19
|
+
def transformers
|
20
|
+
view_transformers.empty? ? Blueprinter.configuration.default_transformers : view_transformers
|
21
|
+
end
|
22
|
+
|
23
|
+
def track_definition_order(method, is_view = true)
|
24
|
+
if @sort_by_definition
|
25
|
+
@definition_order << DefinitionPlaceholder.new(method, is_view)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def inherit(view)
|
30
|
+
view.fields.values.each do |field|
|
31
|
+
self << field
|
32
|
+
end
|
33
|
+
|
34
|
+
view.included_view_names.each do |view_name|
|
35
|
+
include_view(view_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
view.excluded_field_names.each do |field_name|
|
39
|
+
exclude_field(field_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
view.view_transformers.each do |transformer|
|
43
|
+
add_transformer(transformer)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def include_view(view_name)
|
48
|
+
track_definition_order(view_name)
|
49
|
+
included_view_names << view_name
|
50
|
+
end
|
51
|
+
|
52
|
+
def include_views(view_names)
|
53
|
+
view_names.each do |view_name|
|
54
|
+
track_definition_order(view_name)
|
55
|
+
included_view_names << view_name
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def exclude_field(field_name)
|
60
|
+
excluded_field_names << field_name
|
61
|
+
end
|
62
|
+
|
63
|
+
def exclude_fields(field_names)
|
64
|
+
field_names.each do |field_name|
|
65
|
+
excluded_field_names << field_name
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_transformer(custom_transformer)
|
70
|
+
view_transformers << custom_transformer
|
71
|
+
end
|
72
|
+
|
73
|
+
def <<(field)
|
74
|
+
track_definition_order(field.name,false)
|
75
|
+
fields[field.name] = field
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Blueprinter
|
4
|
+
# @api private
|
5
|
+
class ViewCollection
|
6
|
+
attr_reader :views, :sort_by_definition
|
7
|
+
def initialize
|
8
|
+
@views = {
|
9
|
+
identifier: View.new(:identifier),
|
10
|
+
default: View.new(:default)
|
11
|
+
}
|
12
|
+
@sort_by_definition = Blueprinter.configuration.sort_fields_by.eql?(:definition)
|
13
|
+
end
|
14
|
+
|
15
|
+
def inherit(view_collection)
|
16
|
+
view_collection.views.each do |view_name, view|
|
17
|
+
self[view_name].inherit(view)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_view?(view_name)
|
22
|
+
views.has_key? view_name
|
23
|
+
end
|
24
|
+
|
25
|
+
def fields_for(view_name)
|
26
|
+
return identifier_fields if view_name == :identifier
|
27
|
+
|
28
|
+
fields, excluded_fields = sortable_fields(view_name)
|
29
|
+
sorted_fields = sort_by_definition ? sort_by_def(view_name, fields) : fields.values.sort_by(&:name)
|
30
|
+
|
31
|
+
(identifier_fields + sorted_fields).reject { |field| excluded_fields.include?(field.name) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def transformers(view_name)
|
35
|
+
views[view_name].transformers
|
36
|
+
end
|
37
|
+
|
38
|
+
def [](view_name)
|
39
|
+
@views[view_name] ||= View.new(view_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def identifier_fields
|
45
|
+
views[:identifier].fields.values
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param [String] view_name
|
49
|
+
# @return [Array<(Hash, Hash<String, NilClass>)>] fields, excluded_fields
|
50
|
+
def sortable_fields(view_name)
|
51
|
+
excluded_fields = {}
|
52
|
+
fields = views[:default].fields
|
53
|
+
views[view_name].included_view_names.each do |included_view_name|
|
54
|
+
next if view_name == included_view_name
|
55
|
+
|
56
|
+
view_fields, view_excluded_fields = sortable_fields(included_view_name)
|
57
|
+
fields = merge_fields(fields, view_fields)
|
58
|
+
excluded_fields.merge!(view_excluded_fields)
|
59
|
+
end
|
60
|
+
fields = merge_fields(fields, views[view_name].fields)
|
61
|
+
|
62
|
+
views[view_name].excluded_field_names.each { |name| excluded_fields[name] = nil }
|
63
|
+
|
64
|
+
[fields, excluded_fields]
|
65
|
+
end
|
66
|
+
|
67
|
+
# select and order members of fields according to traversal of the definition_orders
|
68
|
+
def sort_by_def(view_name, fields)
|
69
|
+
ordered_fields = {}
|
70
|
+
views[:default].definition_order.each { |definition| add_to_ordered_fields(ordered_fields, definition, fields, view_name) }
|
71
|
+
ordered_fields.values
|
72
|
+
end
|
73
|
+
|
74
|
+
# view_name_filter allows to follow definition order all the way down starting from the view_name given to sort_by_def()
|
75
|
+
# but include no others at the top-level
|
76
|
+
def add_to_ordered_fields(ordered_fields, definition, fields, view_name_filter = nil)
|
77
|
+
if definition.view?
|
78
|
+
if view_name_filter.nil? || view_name_filter == definition.name
|
79
|
+
views[definition.name].definition_order.each { |_definition| add_to_ordered_fields(ordered_fields, _definition, fields) }
|
80
|
+
end
|
81
|
+
else
|
82
|
+
ordered_fields[definition.name] = fields[definition.name]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def merge_fields(source_fields, included_fields)
|
87
|
+
source_fields.merge included_fields
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/blueprinter.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Blueprinter
|
4
|
+
module Generators
|
5
|
+
class BlueprintGenerator < ::Rails::Generators::NamedBase
|
6
|
+
desc "Generates blueprint for ActiveRecord model with the given NAME."
|
7
|
+
|
8
|
+
attr_accessor :options
|
9
|
+
|
10
|
+
source_root File.expand_path("../templates", __FILE__)
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
class_option :blueprints_dir, default: "app/blueprints", desc: "path to new blueprint", aliases: "-d"
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
class_option :identifier, default: nil, desc: "Add an identifer to the generated blueprint, either uses :id or your specified value", aliases: "-i", banner: "id"
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
class_option :fields, type: :array, default: [], desc: "Manually add specified fields"
|
23
|
+
|
24
|
+
class_option :detect_fields, type: :boolean, default: false, desc: "Introspect on the model to set fields in the generated blueprint. Will be merged with any manually specified"
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
class_option :associations, type: :array, default: [], desc: "Manually add specified associations", aliases: "-a"
|
29
|
+
|
30
|
+
class_option :detect_associations, type: :boolean, default: false, desc: "Introspect on the model to set associations in the generated blueprint. Will be merged with any manually specified"
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
class_option :wrap_at, type: :numeric, default: 80, desc: "Maximum length of generated fields line", aliases: "-w"
|
35
|
+
|
36
|
+
class_option :indentation, type: :string, default: "two", desc: "Indentation of generated file", banner: "two|four|tab"
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
remove_class_option :skip_namespace
|
41
|
+
|
42
|
+
def ensure_blueprint_dir
|
43
|
+
FileUtils.mkdir_p(path) unless File.directory?(path)
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_blueprint
|
47
|
+
template "blueprint.rb", File.join(path, "#{file_path}_blueprint.rb")
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def path
|
55
|
+
options["blueprints_dir"]
|
56
|
+
end
|
57
|
+
|
58
|
+
def identifier_symbol
|
59
|
+
if options['identifier']
|
60
|
+
options['identifier'] == "identifier" ? :id : options['identifier']
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def fields
|
65
|
+
fs = if options["detect_fields"]
|
66
|
+
Array.new(options["fields"]).concat(introspected_fields)
|
67
|
+
else
|
68
|
+
options["fields"]
|
69
|
+
end
|
70
|
+
fs.reject {|f| f.blank? }.uniq
|
71
|
+
end
|
72
|
+
|
73
|
+
def introspected_fields
|
74
|
+
class_name.constantize.columns_hash.keys
|
75
|
+
end
|
76
|
+
|
77
|
+
# split at wrap_at chars, two indentations
|
78
|
+
def formatted_fields
|
79
|
+
two_indents = indent * 2
|
80
|
+
fields_string = fields.reduce([]) do |memo, f|
|
81
|
+
if !memo.last.nil?
|
82
|
+
now = "#{memo.last} :#{f},"
|
83
|
+
if now.length > options["wrap_at"].to_i
|
84
|
+
memo << ":#{f},"
|
85
|
+
else
|
86
|
+
memo[memo.length - 1] = now
|
87
|
+
end
|
88
|
+
else
|
89
|
+
memo << " :#{f},"
|
90
|
+
end
|
91
|
+
memo
|
92
|
+
end.join("\n#{two_indents}")
|
93
|
+
|
94
|
+
fields_string[0,fields_string.length - 1]
|
95
|
+
end
|
96
|
+
|
97
|
+
def associations
|
98
|
+
as = if options["detect_associations"]
|
99
|
+
Array.new(options["associations"]).concat(introspected_associations.keys)
|
100
|
+
else
|
101
|
+
options["associations"]
|
102
|
+
end
|
103
|
+
as.reject {|f| f.blank? }.uniq
|
104
|
+
end
|
105
|
+
|
106
|
+
def introspected_associations
|
107
|
+
class_name.constantize.reflections
|
108
|
+
end
|
109
|
+
|
110
|
+
def association_blueprint(association_name)
|
111
|
+
", blueprint: #{association_class(association_name)}"
|
112
|
+
end
|
113
|
+
|
114
|
+
def association_class(association_name)
|
115
|
+
introspected_name = if introspected_associations[association_name].respond_to?(:klass)
|
116
|
+
introspected_associations[association_name].klass.to_s
|
117
|
+
else
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
"#{introspected_name || association_name.camelcase}Blueprint"
|
121
|
+
end
|
122
|
+
|
123
|
+
def indent
|
124
|
+
user_intended = {two: " ", four: " ", tab:"\t"}[options["indentation"].intern]
|
125
|
+
user_intended.nil? ? " " : user_intended
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= class_name %>Blueprint < Blueprinter::Base
|
4
|
+
<% if identifier_symbol -%>
|
5
|
+
<%= indent -%>identifier :<%= identifier_symbol %>
|
6
|
+
|
7
|
+
<% end -%>
|
8
|
+
<% if fields.any? -%>
|
9
|
+
<%= indent -%>fields<%= formatted_fields %>
|
10
|
+
|
11
|
+
<% end -%>
|
12
|
+
<% associations.each do |a| -%>
|
13
|
+
<%= indent -%>association :<%= a -%><%= association_blueprint(a) %>
|
14
|
+
|
15
|
+
<% end -%>
|
16
|
+
end
|