Class: SvelteOnRails::Lib::ViewHelperSupport

Inherits:
Object
  • Object
show all
Defined in:
lib/svelte_on_rails/lib/view_helper_support.rb

Constant Summary collapse

RENDER_OPTIONS =
{ ssr: [true, false, :auto], hydrate: :bool, debug: :bool, cache_key: :string_array, expires_in: :integer, cached: :bool }

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(component, props, html_options, options, request, view_dir, config, caching = false, start_time) ⇒ ViewHelperSupport

Returns a new instance of ViewHelperSupport.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/svelte_on_rails/lib/view_helper_support.rb', line 10

def initialize(
  component, props, html_options, options,
  request, view_dir, config, caching = false, start_time
)

  @start_time = start_time
  @conf = config
  @utils = SvelteOnRails::Lib::Utils
  @options = options
  @component_name = component
  @view_dir = view_dir
  @request = request
  @props = props
  @html_options = html_options

  @ssr = resolve_ssr?(request)
  return unless @ssr

  if @conf.watch_changes?

    # check file existence and capitalization

    p = component_paths
    unless File.exist?(Rails.root.join(p[:path]))
      raise <<~TEXT
      [SOR] Source file not found: «#{p[:path]}»

      TEXT
    end
    if p[:path][0..9] == 'app/views/'
      if !p[:file_basename].match?(/^[A-Z][A-Za-z0-9]+$/) && !p[:file_basename].match?(/^[a-z0-9][a-z0-9_]+$/)
        raise <<~TEXT
        [SOR] Invalid filename: «#{p[:file_basename]}»
        Component names must be CamelCased or under_scored and must be prefixed by underscore.

        TEXT
      end
    elsif !p[:file_basename].match?(/^[A-Z][A-Za-z0-9]+$/)
      raise <<~TEXT
      [SOR] Invalid filename: «#{p[:file_basename]}»
      Component names must be CamelCased.
      TEXT
    end
    full_path = Rails.root.join(p[:path])
    dir, wanted = full_path.dirname.to_s, full_path.basename.to_s
    unless Dir.children(dir).include?(wanted)
      raise "ERROR: Incorrect capitalisation: «#{p[:path]}»"
    end


    if !@conf.manifest_json_path.exist? ||
       !@conf.dev_module_map_path.exist? ||

       @conf.build_status['mtime'].to_f != @utils.manifest_file_mtime ||
       # if a developer has run build from the console, we must update the build-status object
       # this is importand for having reliable error-messages

       (!@conf.build_status['passed'] && !request_metrics[:build_tried_for_error_message])
      # when build has failed the developer sees the error message
      # at this point it is importand to see the build-error in the logs
      # but only ONCE for a request, so we check metrics too

      run_vite_build
      set_request_metrics(:build_tried_for_error_message)

      return unless @conf.build_status['passed']
    end

    # now we are sure to have a valid manifest
    # and are able to collect the dependend source files,
    # check theyr mtime
    # and run a build if something has changed (is only the case if the build above not was run)
    ensure_valid_component_and_manifest!(component)
    if @conf.component_mtime_changed?(component_paths[:path], debug?, component_paths[:file_basename])
      run_vite_build
    end

    # finally, validate the given options
    validate_options

  end

  if caching
    @cached_content = @conf.debug_log(debug?, component_paths[:name], "Fetch Cache") do
      ck = (@conf.watch_changes? ? cache_key_fresh : cache_key)
      @conf.redis_instance.get(ck)
    end
    return if @cached_content
  elsif @conf.watch_changes?
    raise '[SOR] :expires_in is not allowed for this helper' if @options.key?(:expires_in)
    raise '[SOR] :cache_key is not allowed for this helper' if @options.key?(:cache_key)
  end

end

Instance Attribute Details

#confObject (readonly)

Returns the value of attribute conf.



8
9
10
# File 'lib/svelte_on_rails/lib/view_helper_support.rb', line 8

def conf
  @conf
end

Instance Method Details

#component_pathsObject



105
106
107
108
109
110
111
112
113
# File 'lib/svelte_on_rails/lib/view_helper_support.rb', line 105

def component_paths
  if @conf.watch_changes?
    @component_paths ||= @utils.component_paths_uncached(@component_name, @view_dir)
  else
    key = "#{@component_name}_#{@view_dir}"
    @conf.component_paths_cache[key] ||= @utils.component_paths_uncached(@component_name, @view_dir)
  end

end

#debug?Boolean

Returns:

  • (Boolean)


141
142
143
# File 'lib/svelte_on_rails/lib/view_helper_support.rb', line 141

def debug?
  @options[:debug]
end

#log_completed(message) ⇒ Object



149
150
151
152
153
# File 'lib/svelte_on_rails/lib/view_helper_support.rb', line 149

def log_completed(message)
  ms = ((Time.now - @start_time) * 1000)
  @conf.request_metrics[:total_time] += ms
  Rails.logger.info "  [SOR] #{component_paths[:name]}.svelte #{message} (#{ms.round(1)}ms)"
end

#render_cached(view_context, &block) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/svelte_on_rails/lib/view_helper_support.rb', line 155

def render_cached(view_context, &block)

  # increase expired time

  @conf.redis_instance.expire(cache_key, redis_expiration_seconds)

  # render

  res = if @cached_content
          msg = "returned from cache"
          @cached_content.html_safe
        else
          msg = "Rendered"
          @skip_caching = false
          @conf.debug_log(debug?, component_paths[:name], "rendered and stored to cache (key: «#{cache_key}»)") do
            r = view_context.instance_eval(&block)
            unless @skip_caching
              @conf.redis_instance.set(cache_key, r)
            end
            r
          end
        end
  log_completed(msg)
  res

end

#request_metricsObject



182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/svelte_on_rails/lib/view_helper_support.rb', line 182

def request_metrics
  @conf.request_metrics ||= {}
  _req_id = @conf.request_metrics[:request_uuid]

  if @request.uuid != _req_id
    @conf.request_metrics = {
      request_uuid: @request.uuid,
      total_time: 0.0
    }
  end

  @conf.request_metrics
end

#set_request_metrics(action_key) ⇒ Object



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/svelte_on_rails/lib/view_helper_support.rb', line 196

def set_request_metrics(action_key)

  if @conf.watch_changes? &&
     ![
       :build,
       :build_failed,
       :render_failed,
       :from_cache,
       :empty,
       :rendered,
       :build_tried_for_error_message,
       :ssr_server_unreachable
     ].include?(action_key)
    raise "[SOR] set_request_metrics: invalid action_key"
  end

  request_metrics

  @conf.request_metrics[action_key] ||= 0
  @conf.request_metrics[action_key] += 1
end

#ssr?Boolean

Returns:

  • (Boolean)


145
146
147
# File 'lib/svelte_on_rails/lib/view_helper_support.rb', line 145

def ssr?
  @ssr
end

#tag_attributes(html_options, svelte_props) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/svelte_on_rails/lib/view_helper_support.rb', line 115

def tag_attributes(html_options, svelte_props)
  ht_opts = html_options.deep_symbolize_keys.deep_merge(
    {
      data: {
        component: component_paths[:vite_path],
        controller: 'svelte-on-rails'
      }
    }
  )
  cl = "svelte-component #{ht_opts[:class]}".split(' ')
  _cl = 'svelte-' + component_paths[:name].underscore.gsub('_', '-')
  cl.push(_cl) unless cl.include?(_cl)


  tnl = SvelteOnRails::Lib::ToSvelteTranslations.instance
  _tnl = tnl.cached_translations(I18n.locale, :to_svelte_props, nil)
  svelte_props[:_translations] = _tnl if _tnl
  svelte_props[:test] = 'hello'
  #svelte_props = {my: :joke}

  ht_opts[:class] = cl.compact.join(' ')
  ht_opts[:data][:props] = svelte_props.to_json
  ht_opts[:data][:svelte_status] = 'do-not-hydrate-me' if @options[:hydrate] == false
  ht_opts
end