Module: RubyUIConverter::RailsHelpers

Defined in:
lib/ruby_ui_converter/rails_helpers.rb

Overview

Best-effort translation of common Rails view helpers found inside <%= %> into Phlex/RubyUI equivalents. Anything not understood is emitted as a bare call (phlex-rails registers these as output helpers that write to the buffer themselves) or, for the few that return a string, through a raw call.

Constant Summary collapse

HTML_HELPERS =

Rails helpers that produce HTML. Under phlex-rails these are output helpers (they write to the buffer and return nil), so an unmapped one is emitted as a bare call — except STRING_HELPERS below, which return a value.

%w[
  link_to button_to image_tag video_tag audio_tag content_tag tag
  form_with form_for fields_for label_tag text_field_tag mail_to
  link_to_unless link_to_if sanitize simple_format raw safe_join
  time_tag favicon_link_tag stylesheet_link_tag javascript_include_tag
].freeze
STRING_HELPERS =

The subset of HTML_HELPERS that RETURN a string instead of writing to the Phlex buffer, so they must be wrapped in a raw call to appear in the output.

%w[sanitize safe_join raw strip_tags].freeze
KNOWN_HELPERS =

Helpers that return plain (already-escaped or scalar) values.

%w[
  t l translate localize number_to_currency number_with_delimiter
  number_to_percentage pluralize truncate current_user current_page?
  params session flash request cookies asset_path image_path url_for
  polymorphic_path time_ago_in_words distance_of_time_in_words
  dom_id dom_class notice alert content_for cycle render
].freeze

Class Method Summary collapse

Class Method Details

.build_locals(arg_list) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 112

def build_locals(arg_list)
  return nil if arg_list.nil? || arg_list.empty?

  pairs = arg_list.map do |arg|
    if arg =~ /\Alocals:\s*\{(.*)\}\z/m
      Regexp.last_match(1).strip
    else
      arg
    end
  end

  result = pairs.reject(&:empty?).join(", ")
  result.empty? ? nil : result
end

.content_tag_call(code) ⇒ Object

content_tag(:div, “hi”, class: “x”) -> div(class: “x”) { “hi” }



163
164
165
166
167
168
169
170
171
172
173
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 163

def (code)
  rest = strip_parens(code.sub(/\Acontent_tag\b/, "").strip)
  args = split_args(rest)
  return nil if args.length < 2

  name = args[0].sub(/\A:/, "").gsub(/['"]/, "")
  content = args[1]
  options = args[2..] || []
  call = options.empty? ? name : "#{name}(#{options.join(", ")})"
  "#{call} { #{content} }"
end

.html_helper?(code) ⇒ Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 77

def html_helper?(code)
  matches_helper?(code, HTML_HELPERS)
end

.image_tag_call(code) ⇒ Object

image_tag “logo.png”, alt: “Logo” -> img(src: “logo.png”, alt: “Logo”)



152
153
154
155
156
157
158
159
160
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 152

def image_tag_call(code)
  rest = strip_parens(code.sub(/\Aimage_tag\b/, "").strip)
  args = split_args(rest)
  return nil if args.empty?

  src, *options = args
  attrs = ["src: #{src}"] + options
  "img(#{attrs.join(", ")})"
end

Targets that are not strings or route helper calls (e.g. a record: ‘link_to “Show”, user`) need url_for to resolve to a path.



144
145
146
147
148
149
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 144

def link_target(path)
  return path if path.start_with?('"', "'") # literal string
  return path if path =~ /\A(\w+_)?(path|url)\b/ # route helper call

  "url_for(#{path})"
end

link_to “Text”, path, class: “x” -> a(href: path, class: “x”) { “Text” } With ruby_ui enabled -> Link(href: path, class: “x”) { “Text” }



129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 129

def link_to_call(code, ruby_ui: false)
  return nil if code =~ /\bdo\b/ # block form handled elsewhere

  rest = strip_parens(code.sub(/\Alink_to\b/, "").strip)
  args = split_args(rest)
  return nil if args.length < 2

  text, path, *options = args
  attrs = ["href: #{link_target(path)}"] + options
  call = ruby_ui ? "Link" : "a"
  "#{call}(#{attrs.join(", ")}) { #{text} }"
end

.matches_helper?(code, helpers) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
88
89
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 85

def matches_helper?(code, helpers)
  helpers.any? do |helper|
    code == helper || code.start_with?("#{helper}(") || code.start_with?("#{helper} ")
  end
end

.render_call(code, transformer) ⇒ Object

render “shared/header” / render partial: “x”, locals: .. / render “form”, a: 1



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 92

def render_call(code, transformer)
  rest = strip_parens(code.sub(/\Arender\b/, "").strip)
  args = split_args(rest)
  return nil if args.empty?

  first = args[0]
  first = Regexp.last_match(1).strip if first =~ /\Apartial:\s*(.+)\z/m

  match = first.match(/\A["']([^"']+)["']\z/)
  return nil unless match

  const = Naming.partial_const(
    match[1],
    base_namespace: transformer.base_namespace,
    current_namespace_parts: transformer.current_namespace_parts
  )
  locals = build_locals(args[1..])
  locals ? "render #{const}.new(#{locals})" : "render #{const}.new"
end

.safeObject



224
225
226
227
228
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 224

def safe
  yield
rescue StandardError
  nil
end

.split_args(string) ⇒ Object

Splits a top-level argument list, respecting strings and nested brackets.



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 176

def split_args(string)
  args = []
  depth = 0
  current = +""
  quote = nil

  string.each_char do |char|
    if quote
      current << char
      quote = nil if char == quote
      next
    end

    case char
    when '"', "'"
      quote = char
      current << char
    when "(", "[", "{"
      depth += 1
      current << char
    when ")", "]", "}"
      depth -= 1
      current << char
    when ","
      if depth.zero?
        args << current.strip
        current = +""
      else
        current << char
      end
    else
      current << char
    end
  end

  args << current.strip unless current.strip.empty?
  args
end

.string_helper?(code) ⇒ Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 81

def string_helper?(code)
  matches_helper?(code, STRING_HELPERS)
end

.strip_parens(string) ⇒ Object



215
216
217
218
219
220
221
222
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 215

def strip_parens(string)
  stripped = string.strip
  if stripped.start_with?("(") && stripped.end_with?(")")
    stripped[1..-2].strip
  else
    stripped
  end
end

.transform(code, node, transformer, builder) ⇒ Object

Attempts to emit a transformed helper. Returns true when it wrote something to the builder, false otherwise.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ruby_ui_converter/rails_helpers.rb', line 36

def transform(code, node, transformer, builder)
  stripped = code.strip

  if stripped == "yield" || stripped =~ /\Ayield\b/
    builder.line(stripped)
    return true
  end

  if stripped.start_with?("render")
    rendered = safe { render_call(stripped, transformer) }
    # phlex-rails' #render handles model objects / relations and writes to
    # the buffer itself, so the object/collection fallback is a bare call.
    builder.line(rendered || stripped)
    return true
  end

  if stripped.start_with?("link_to")
    rendered = safe { link_to_call(stripped, ruby_ui: transformer.config.ruby_ui?) }
    return builder.line(rendered) && true if rendered
  end

  if stripped.start_with?("image_tag")
    rendered = safe { image_tag_call(stripped) }
    return builder.line(rendered) && true if rendered
  end

  if stripped.start_with?("content_tag")
    rendered = safe { (stripped) }
    return builder.line(rendered) && true if rendered
  end

  if html_helper?(stripped)
    # Output helpers write to the buffer (bare call); string-returning ones
    # need a raw call to be emitted.
    builder.line(string_helper?(stripped) ? transformer.config.raw_call(stripped) : stripped)
    return true
  end

  false
end