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