rails_api_documentation 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.
- checksums.yaml +7 -0
- data/README.md +41 -0
- data/app/assets/config/rails_api_doc_manifest.js +2 -0
- data/app/assets/javascripts/api_doc.js +5 -0
- data/app/assets/javascripts/components.js +1 -0
- data/app/assets/javascripts/rails_api_doc/application.js +3 -0
- data/app/assets/javascripts/table.js.coffee +8 -0
- data/app/assets/stylesheets/rails_api_doc/application.css +15 -0
- data/app/assets/stylesheets/rails_api_doc/table.sass +105 -0
- data/app/controllers/rails_api_doc/api_docs_controller.rb +51 -0
- data/app/controllers/rails_api_doc/application_controller.rb +5 -0
- data/app/helpers/rails_api_doc/application_helper.rb +4 -0
- data/app/models/rails_api_doc/application_record.rb +5 -0
- data/app/views/layouts/rails_api_doc/application.slim +20 -0
- data/app/views/rails_api_doc/api_docs/_edit_field.slim +9 -0
- data/app/views/rails_api_doc/api_docs/_request_api_table.slim +43 -0
- data/app/views/rails_api_doc/api_docs/_response_api_table.slim +7 -0
- data/app/views/rails_api_doc/api_docs/_side_menu.slim +10 -0
- data/app/views/rails_api_doc/api_docs/_title.slim +4 -0
- data/app/views/rails_api_doc/api_docs/edit.js.erb +0 -0
- data/app/views/rails_api_doc/api_docs/example.html.erb +32 -0
- data/app/views/rails_api_doc/api_docs/index.slim +23 -0
- data/app/views/rails_api_doc/api_docs/new.js.erb +0 -0
- data/app/views/shared/_response_table.slim +14 -0
- data/app/views/shared/_table.slim +20 -0
- data/config/routes.rb +10 -0
- data/lib/rails_api_doc/config/validator.rb +14 -0
- data/lib/rails_api_doc/configuration.rb +9 -0
- data/lib/rails_api_doc/controller/attribute_parser.rb +56 -0
- data/lib/rails_api_doc/controller/parameter/repository/param.rb +51 -0
- data/lib/rails_api_doc/controller/parameter/repository.rb +33 -0
- data/lib/rails_api_doc/controller/parameter.rb +57 -0
- data/lib/rails_api_doc/controller/response/rabl.rb +58 -0
- data/lib/rails_api_doc/controller/response/rabl_compiler.rb +236 -0
- data/lib/rails_api_doc/controller/response_factory.rb +19 -0
- data/lib/rails_api_doc/controller/strong_params.rb +46 -0
- data/lib/rails_api_doc/controller.rb +6 -0
- data/lib/rails_api_doc/engine.rb +40 -0
- data/lib/rails_api_doc/types.rb +7 -0
- data/lib/rails_api_doc/version.rb +5 -0
- data/lib/rails_api_doc.rb +26 -0
- data/lib/tasks/rails_api_doc_tasks.rake +4 -0
- metadata +197 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
div.flex-table
|
2
|
+
div.flex-line.row
|
3
|
+
/ Угловой елемент
|
4
|
+
div.flex-item Parameter
|
5
|
+
div.flex-item Value
|
6
|
+
- locals[:rows].each do |row_name, node|
|
7
|
+
div.flex-line.row
|
8
|
+
div.flex-item #{row_name}
|
9
|
+
- if node.nested?
|
10
|
+
div.flex-item.next-is-nested #{node.attr}(Nested)
|
11
|
+
- else
|
12
|
+
div.flex-item #{node.attr}
|
13
|
+
- if node.nested?
|
14
|
+
= render 'shared/response_table', locals: { rows: node.nested }
|
@@ -0,0 +1,20 @@
|
|
1
|
+
- nesting = locals[:nesting].to_a.push(locals[:model])
|
2
|
+
|
3
|
+
div.flex-table
|
4
|
+
div.flex-line.row
|
5
|
+
/ Угловой елемент
|
6
|
+
div.flex-item Parameter
|
7
|
+
div.flex-item Type
|
8
|
+
- @request_headers.each_value do |header|
|
9
|
+
div.flex-item = header
|
10
|
+
- locals[:rows].each do |row_name, row_values|
|
11
|
+
div.flex-line.row
|
12
|
+
div.flex-item #{row_name} #{'*' if row_values.required?}
|
13
|
+
- if row_values.nested?
|
14
|
+
div.flex-item.next-is-nested #{row_values[:model]}(Nested)
|
15
|
+
- else
|
16
|
+
div.flex-item #{row_values[:type]}
|
17
|
+
- @request_headers.each_key do |header_alias|
|
18
|
+
div.flex-item = row_values[header_alias]
|
19
|
+
- if row_values.nested?
|
20
|
+
= render 'shared/table', locals: { nesting: nesting, model: row_values[:model] || row_name, rows: row_values[:nested] }
|
data/config/routes.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
class RailsApiDoc::Config::Validator
|
4
|
+
|
5
|
+
cattr_accessor :checkers
|
6
|
+
self.checkers = []
|
7
|
+
|
8
|
+
def self.valid_param?(controller_param, api_param_data)
|
9
|
+
checkers.all? do |checker|
|
10
|
+
checker.valid?(controller_param, api_param_data)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
# :nodoc:
|
4
|
+
class RailsApiDoc::Controller::AttributeParser
|
5
|
+
|
6
|
+
# TODO : Change to I18n. Added on: 08.10.16. Added by: <@vshaveyko>
|
7
|
+
WRONG_NAME_ERROR_STRING = 'Name should consist only of letters\ciphers\underscores'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def parse_attributes(params)
|
12
|
+
type = :enum if params[:enum].present?
|
13
|
+
|
14
|
+
{
|
15
|
+
name: parse_name(params[:name]),
|
16
|
+
type: type || parse_type(params[:type]),
|
17
|
+
enum: parse_enum(params[:enum])
|
18
|
+
}.compact
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def parse_name(name_string)
|
24
|
+
return if name_string.blank?
|
25
|
+
raise ArgumentError, WRONG_NAME_ERROR_STRING unless name_string =~ /[A-z0-9_]*/
|
26
|
+
name_string.underscore.to_sym
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_enum(enum_string)
|
30
|
+
return if enum_string.blank?
|
31
|
+
enum_string.split(',').map do |enum_value|
|
32
|
+
parse_enum_value(enum_value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_enum_value(value)
|
37
|
+
case value
|
38
|
+
when /^\d+$/
|
39
|
+
value.to_i
|
40
|
+
when 'true'
|
41
|
+
true
|
42
|
+
when 'false'
|
43
|
+
false
|
44
|
+
else
|
45
|
+
value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_type(type)
|
50
|
+
return if type.blank?
|
51
|
+
type.constantize
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
class RailsApiDoc::Controller::Parameter::Repository::Param
|
4
|
+
|
5
|
+
ACCEPTED_TYPES = [String, Integer, Object, Array, DateTime, :enum, :model].freeze
|
6
|
+
|
7
|
+
# @type - type to check
|
8
|
+
def self.accepted_nested_type?(type)
|
9
|
+
type == Object || type == :model
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.valid_type?(type)
|
13
|
+
return if type.in?(ACCEPTED_TYPES)
|
14
|
+
raise ArgumentError, "Wrong type: #{type}. " \
|
15
|
+
"Correct types are: #{ACCEPTED_TYPES}."
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.valid_enum?(enum)
|
19
|
+
return if enum.nil? || enum.is_a?(Array)
|
20
|
+
raise ArgumentError, 'Enum must be an array.'
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.valid_nested?(type, block_given)
|
24
|
+
return false unless accepted_nested_type?(type)
|
25
|
+
return true if block_given
|
26
|
+
raise ArgumentError, 'Empty object passed.'
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(name, store)
|
30
|
+
@name = name
|
31
|
+
@store = store
|
32
|
+
end
|
33
|
+
|
34
|
+
def nested?
|
35
|
+
self.class.accepted_nested_type?(@store[:type])
|
36
|
+
end
|
37
|
+
|
38
|
+
def required?
|
39
|
+
@store[:required]
|
40
|
+
end
|
41
|
+
|
42
|
+
def method_missing(name, *args)
|
43
|
+
return @store.send(name, *args) if respond_to_missing?(name)
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
47
|
+
def respond_to_missing?(name)
|
48
|
+
@store.respond_to?(name)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
# :nodoc:
|
4
|
+
class RailsApiDoc::Controller::Parameter::Repository
|
5
|
+
|
6
|
+
@repo = Hash.new { |hsh, key| hsh[key] = Hash.new { |hsh, key| hsh[key] = Param.new(key) } }
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def method_missing(name, *args, &block)
|
11
|
+
return @repo.send(name, *args, &block) if respond_to_missing?(name)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def registered_controllers
|
16
|
+
@repo.keys
|
17
|
+
end
|
18
|
+
|
19
|
+
def respond_to_missing?(method, *)
|
20
|
+
@repo.respond_to?(method)
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(key, value)
|
24
|
+
unless key < ActionController::Base
|
25
|
+
raise ArgumentError, 'Repository keys are controllers only'
|
26
|
+
end
|
27
|
+
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
module RailsApiDoc::Controller::Parameter
|
4
|
+
|
5
|
+
VALID_KEYS = [:type, :required, :enum, :model].freeze #:nodoc:
|
6
|
+
|
7
|
+
# Use parameter in controller to defined REQUEST parameter.
|
8
|
+
# Adds it to repository: RailsApiDoc::Controller::Parameter::Repository
|
9
|
+
def parameter(name, options, &block)
|
10
|
+
raise ArgumentError, 'Parameter already defined.' if repo.key?(name)
|
11
|
+
|
12
|
+
validate_options(options, block_given?)
|
13
|
+
|
14
|
+
define_parameter(name, options, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def validate_options(options, block_given)
|
20
|
+
if options.nil? || options.empty?
|
21
|
+
raise ArgumentError, 'Empty options passed.'
|
22
|
+
end
|
23
|
+
|
24
|
+
options.assert_valid_keys(VALID_KEYS)
|
25
|
+
|
26
|
+
Repository::Param.valid_type?(options[:type])
|
27
|
+
|
28
|
+
Repository::Param.valid_enum?(options[:enum])
|
29
|
+
|
30
|
+
Repository::Param.valid_nested?(options[:type], block_given)
|
31
|
+
end
|
32
|
+
|
33
|
+
# default repo can be reassigned to deal with nested parameters
|
34
|
+
# see nested_parameter
|
35
|
+
def repo
|
36
|
+
@repo || Repository[self]
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_parameter(name, parameter_data, &block)
|
40
|
+
repo[name] = \
|
41
|
+
if Repository::Param.valid_nested?(parameter_data[:type], block_given?)
|
42
|
+
Repository::Param.new(name, nested_parameter(parameter_data, &block))
|
43
|
+
else
|
44
|
+
Repository::Param.new(name, parameter_data)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def nested_parameter(parameter_data)
|
49
|
+
_backup_repo = @repo
|
50
|
+
@repo = {}
|
51
|
+
yield
|
52
|
+
parameter_data.merge(nested: @repo)
|
53
|
+
ensure
|
54
|
+
@repo = _backup_repo
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
# :nodoc:
|
4
|
+
# :nodoc:
|
5
|
+
class RailsApiDoc::Controller::Response
|
6
|
+
|
7
|
+
# :nodoc:
|
8
|
+
class Rabl
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
attr_accessor :renderer
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :map
|
17
|
+
|
18
|
+
# pass all controllers registered for api doc
|
19
|
+
# TODO: add setting for displaying all from start
|
20
|
+
def initialize(controllers)
|
21
|
+
@controllers = controllers
|
22
|
+
@routes = Rails.application.routes.set.anchored_routes.reject { |r| r.defaults[:internal] }
|
23
|
+
@map = construct_controller_template_map
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_template(ctrl, action)
|
27
|
+
RablCompiler.new("#{ctrl.controller_path}/#{action}").compile_source
|
28
|
+
end
|
29
|
+
|
30
|
+
def action_route(ctrl, action)
|
31
|
+
action_route = @map[ctrl][:routes].detect { |r| r.defaults[:action] == action }
|
32
|
+
method = action_route.instance_variable_get(:@request_method_match).first.name.split('::').last
|
33
|
+
route = action_route.path.spec.to_s
|
34
|
+
[method, route].join(' ')
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def construct_controller_template_map
|
40
|
+
@controllers.each_with_object({}) do |ctrl, map|
|
41
|
+
map[ctrl] = ctrl_actions(ctrl)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def ctrl_actions(ctrl)
|
46
|
+
routes = @routes.select do |route|
|
47
|
+
route.defaults[:controller].in?([ctrl.controller_path, ctrl.controller_name])
|
48
|
+
end
|
49
|
+
actions = routes.map { |r| r.defaults[:action] }
|
50
|
+
{
|
51
|
+
routes: routes,
|
52
|
+
actions: ctrl.action_methods
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# author: Vadim Shaveiko <@vshaveyko>
|
3
|
+
# :nodoc:
|
4
|
+
# :nodoc:
|
5
|
+
class RailsApiDoc::Controller::Response
|
6
|
+
|
7
|
+
class Node < Struct.new(:name, :attr, :nested)
|
8
|
+
|
9
|
+
def nested?
|
10
|
+
!nested.nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
# template struct
|
16
|
+
class CompiledAttributes
|
17
|
+
|
18
|
+
attr_accessor :nodes, :data, :root_name
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@nodes = {}
|
22
|
+
# @cache_key = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def each(&block)
|
26
|
+
@nodes.each &block
|
27
|
+
end
|
28
|
+
|
29
|
+
# def initialize_dup(other)
|
30
|
+
# super
|
31
|
+
# self.nodes = other.nodes.dup
|
32
|
+
# end
|
33
|
+
|
34
|
+
def add(n)
|
35
|
+
if n[:attr] && n[:name] != n[:attr]
|
36
|
+
n[:name] = "#{n[:name]}(#{n[:attr]})"
|
37
|
+
end
|
38
|
+
|
39
|
+
@nodes[n[:name]] = Node.new(n[:name], n[:attr], n[:nested])
|
40
|
+
end
|
41
|
+
|
42
|
+
def extends(template)
|
43
|
+
@nodes.merge! template.nodes
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
# Class that will compile RABL source code into a hash
|
48
|
+
# representing data structure
|
49
|
+
class RablCompiler
|
50
|
+
|
51
|
+
def initialize(file_path, view_path: 'app/views')
|
52
|
+
paths = Dir["#{view_path}/#{file_path}{.json,}.rabl"]
|
53
|
+
file_path = paths.find { |path| File.exist?(path) }
|
54
|
+
|
55
|
+
@source = _preserve_ivars File.read(file_path)
|
56
|
+
rescue
|
57
|
+
end
|
58
|
+
|
59
|
+
# find all instance variables used and prepend them with ':' to
|
60
|
+
# turn into symbols on compile. otherwise they turn to nil(random ivar is nil)
|
61
|
+
def _preserve_ivars(source)
|
62
|
+
source.gsub /(@[^\s]*)/, ':\1'
|
63
|
+
end
|
64
|
+
|
65
|
+
# Compile from source code and return the CompiledAttributes created.
|
66
|
+
def compile_source
|
67
|
+
return unless @source
|
68
|
+
@attributes = CompiledAttributes.new
|
69
|
+
instance_eval(@source)
|
70
|
+
@attributes
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Sets the object to be used as the data for the template
|
75
|
+
# Example:
|
76
|
+
# object :@user
|
77
|
+
# object :@user, :root => :author
|
78
|
+
#
|
79
|
+
# dont care about object / collection options. need only attributes
|
80
|
+
def object(a)
|
81
|
+
@attributes.data = a
|
82
|
+
end
|
83
|
+
alias collection object
|
84
|
+
|
85
|
+
#
|
86
|
+
# Includes the attribute or method in the output
|
87
|
+
# Example:
|
88
|
+
# attributes :id, :name
|
89
|
+
# attribute :email => :super_secret
|
90
|
+
#
|
91
|
+
# save attribute to nodes
|
92
|
+
def attribute(*args)
|
93
|
+
if args.first.is_a?(Hash)
|
94
|
+
args.first.each_pair do |key, name|
|
95
|
+
@attributes.add(name: name, attr: key)
|
96
|
+
end
|
97
|
+
else
|
98
|
+
options = args.extract_options!
|
99
|
+
args.each do |name|
|
100
|
+
key = options[:as] || name
|
101
|
+
@attributes.add(name: name, attr: key)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
alias attributes attribute
|
106
|
+
|
107
|
+
#
|
108
|
+
# Creates a child node to be included in the output.
|
109
|
+
# name_or data can be an object or collection or a method to call on the data. It
|
110
|
+
# accepts :root and :partial options.
|
111
|
+
# Notes that partial and blocks are not compatible
|
112
|
+
# Example:
|
113
|
+
# child(:@posts, :root => :posts) { attribute :id }
|
114
|
+
# child(:posts, :partial => 'posts/base')
|
115
|
+
# child(:@posts => :cool_posts, :root => :posts) { attribute :id }
|
116
|
+
#
|
117
|
+
def child(name_or_data, options = {})
|
118
|
+
data, name = extract_data_and_name(name_or_data)
|
119
|
+
|
120
|
+
if options.empty? && new_options = name_or_data.to_a.second
|
121
|
+
options = [new_options].to_h
|
122
|
+
end
|
123
|
+
|
124
|
+
name = options[:root] if options.key? :root
|
125
|
+
|
126
|
+
if options.key?(:partial)
|
127
|
+
attrs = RablCompiler.new(options[:partial]).compile_source
|
128
|
+
elsif block_given?
|
129
|
+
attrs = sub_compile(data) { yield }
|
130
|
+
end
|
131
|
+
|
132
|
+
@attributes.add(name: name, attr: data, nested: attrs.nodes)
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Glues data from a child node to the output
|
137
|
+
# Example:
|
138
|
+
# glue(:@user) { attribute :name }
|
139
|
+
#
|
140
|
+
def glue(data)
|
141
|
+
return unless block_given?
|
142
|
+
|
143
|
+
template = sub_compile(data) { yield }
|
144
|
+
|
145
|
+
template.nodes.each_value do |value|
|
146
|
+
@attributes.add name: value.name, attr: "#{data}.#{value.attr}", nested: value.nested
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# Creates an arbitrary node in the json output.
|
152
|
+
# It accepts :if option to create conditionnal nodes. The current data will
|
153
|
+
# be passed to the block so it is advised to use it instead of ivars.
|
154
|
+
# Example:
|
155
|
+
# node(:name) { |user| user.first_name + user.last_name }
|
156
|
+
# node(:role, if: ->(u) { !u.admin? }) { |u| u.role }
|
157
|
+
#
|
158
|
+
def node(name = nil, _options = {})
|
159
|
+
return unless block_given?
|
160
|
+
@attributes.add name: name
|
161
|
+
end
|
162
|
+
alias code node
|
163
|
+
|
164
|
+
#
|
165
|
+
# Merge arbitrary data into json output. Given block should
|
166
|
+
# return a hash.
|
167
|
+
# Example:
|
168
|
+
# merge { |item| partial("specific/#{item.to_s}", object: item) }
|
169
|
+
#
|
170
|
+
# def merge
|
171
|
+
# return unless block_given?
|
172
|
+
# yield
|
173
|
+
# end
|
174
|
+
|
175
|
+
#
|
176
|
+
# Extends an existing rabl template
|
177
|
+
# Example:
|
178
|
+
# extends 'users/base'
|
179
|
+
#
|
180
|
+
def extends(path)
|
181
|
+
extended = RablCompiler.new(path).compile_source
|
182
|
+
@attributes.extends extended
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Provide a conditionnal block
|
187
|
+
#
|
188
|
+
# condition(->(u) { u.is_a?(Admin) }) do
|
189
|
+
# attributes :secret
|
190
|
+
# end
|
191
|
+
#
|
192
|
+
# def condition(*)
|
193
|
+
# return unless block_given?
|
194
|
+
# template = sub_compile(nil, true) { yield }
|
195
|
+
# @attributes.extends template
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
def method_missing(name, *attrs)
|
199
|
+
return p("#{name} is not implemented in railsApiDoc") if name.in?(['merge', 'condtiion'])
|
200
|
+
super
|
201
|
+
end
|
202
|
+
|
203
|
+
protected
|
204
|
+
|
205
|
+
#
|
206
|
+
# Extract data root_name and root name
|
207
|
+
# Example:
|
208
|
+
# :@users -> [:@users, nil]
|
209
|
+
# :@users => :authors -> [:@users, :authors]
|
210
|
+
#
|
211
|
+
def extract_data_and_name(name_or_data)
|
212
|
+
case name_or_data
|
213
|
+
when Symbol
|
214
|
+
str = name_or_data.to_s
|
215
|
+
str.start_with?('@') ? [name_or_data, str[1..-1]] : [name_or_data, name_or_data]
|
216
|
+
when Hash
|
217
|
+
name_or_data.first
|
218
|
+
else
|
219
|
+
name_or_data
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def sub_compile(data, only_nodes = false)
|
224
|
+
raise unless block_given?
|
225
|
+
old_template = @attributes
|
226
|
+
@attributes = CompiledAttributes.new
|
227
|
+
yield
|
228
|
+
@attributes.data = data
|
229
|
+
only_nodes ? @attributes.nodes : @attributes
|
230
|
+
ensure
|
231
|
+
@attributes = old_template
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|