vue-helpers 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.
@@ -0,0 +1,21 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ # Adds cli options for minitest. These can also be passed from rake cli with TESTOPTS='-v ...'
5
+ # Also see https://chriskottom.com/blog/2014/12/command-line-flags-for-minitest-in-the-raw/
6
+ ENV['TESTOPTS'] ||= '-v'
7
+
8
+
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << "test"
11
+ t.libs << "lib"
12
+ t.test_files = FileList["test/**/*_test.rb"]
13
+
14
+ # Rake 11 turns on ruby warnings by default.
15
+ # This will turn them off again on the cli:
16
+ # RUBYOPT=W0
17
+ # Also see https://github.com/hanami/utils/issues/123
18
+ t.warning = false
19
+ end
20
+
21
+ task :default => :test
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "vue/helpers"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ require "irb/completion"
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,49 @@
1
+ require "vue/helpers/version"
2
+
3
+ # See below...
4
+ # require_relative 'helpers/methods'
5
+ # require_relative 'helpers/server'
6
+
7
+ module Vue
8
+ module Helpers
9
+
10
+ @defaults = {
11
+ cache_store: {},
12
+ callback_prefix: '/vuecallback',
13
+ default_outvar: '@_erbout',
14
+ external_resource: false,
15
+ minify: false,
16
+ register_local: false,
17
+ root_name: 'vue-root',
18
+ template_engine: :erb, # This should not force template_engine, only recommend if no other can be found.
19
+ template_literal: true,
20
+ views_path: ['views', 'app/views'],
21
+ vue_outvar: '@_vue_outvar',
22
+
23
+ # These are ugly now, becuase I wanted to use double-quotes as the outter-quotes.
24
+ component_call_html: "<\#{el_name} \#{attributes_string}>\#{block_content}</\#{el_name}>",
25
+ external_resource_html: "\n<script src=\"\#{callback_prefix}/\#{key}\"></script>",
26
+ inline_script_html: "\n<script>\#{compiled}\n</script>\n",
27
+ root_app_html: '<div id="#{root_name}">#{block_content}</div>#{root_script_output}',
28
+ root_object_js: 'var #{app_name} = new Vue({el: ("##{root_name}"), components: {#{components}}, data: #{vue_data_json}})',
29
+ x_template_html: "\n<script type=\"text/x-template\" id=\"\#{name}-template\">\#{template}</script>",
30
+ }
31
+
32
+ @defaults.keys.each do |k|
33
+ define_singleton_method(k){@defaults[k]}
34
+ define_singleton_method("#{k}="){|v| @defaults[k] = v}
35
+ end
36
+
37
+ class << self
38
+ attr_accessor :defaults
39
+ end
40
+
41
+ def self.included(other)
42
+ other.send(:include, Vue::Helpers::Methods)
43
+ end
44
+
45
+ end
46
+ end
47
+
48
+ require_relative 'helpers/methods'
49
+ require_relative 'helpers/server'
@@ -0,0 +1,219 @@
1
+ require 'uglifier'
2
+ require_relative 'utilities'
3
+
4
+ # This file contains the bulk of vue-helpers functions.
5
+ # They constructed here as a module of refinements,
6
+ # and used in the main 'controller'. However, being refinements,
7
+ # they are invisible and inaccessible in user-space.
8
+ #
9
+ # The funky self-referencing refinement code on this page is
10
+ # necessary for these refinement methods to see each other.
11
+ # Remember... lexical scope: a refined method call must have
12
+ # 'using...' somewhere on the same page (or sometimes in the same module).
13
+ # So these refinements must refine themselves.
14
+ #
15
+ # This actually works, even with the self-refiment at the bottom of the module.
16
+ #
17
+ # See Ruby 2.4 release notes & changelog for more info on the refinement features used here:
18
+ # https://github.com/ruby/ruby/blob/v2_4_0/NEWS
19
+ #
20
+ module Vue
21
+ module Helpers
22
+ module Methods
23
+ end
24
+
25
+ # This block of methods is used as refinements in Ruby >= 2.4,
26
+ # and is used as a regular module methods (private) in Ruby < 2.4.
27
+ # This is done to accommodate a wider range of Ruby versions,
28
+ # since Ruby < 2.4 doesn't allow refining of Modules.
29
+ # See if-then block below.
30
+ #
31
+ MethodsBlock = Proc.new do
32
+
33
+ # This has to be here, NOT above under HelperRefinements.
34
+ using CoreRefinements
35
+
36
+ # Can't be private, since VueObject instances call these methods.
37
+ #private
38
+
39
+ # TODO: Cleanup Load/Render template calls upstream, then cleanup these methods here.
40
+ # These are a mess, since they were hacked together when their functionality was split up.
41
+ def render_ruby_template(template_text_or_file, template_engine:nil, locals:{})
42
+ #puts " RENDERING ruby template '#{template_text_or_file.to_s[0..32].gsub(/\n/, ' ')}' with locals:#{locals}, template_engine:#{template_engine}, Tilt.current_tempate: '#{Tilt.current_template}'\n"
43
+
44
+ tilt_template = load_template(template_text_or_file, template_engine:nil)
45
+
46
+ rslt = if tilt_template.is_a?(Tilt::Template)
47
+ #puts " Rendering #{tilt_template}"
48
+ #puts " Rendering ruby template '#{template_text_or_file.to_s[0..32].gsub(/\n/, ' ')}...' from '#{tilt_template.file}' with locals:#{locals}, template_engine:#{template_engine}, Tilt.current_tempate: '#{Tilt.current_template}'"
49
+ #puts " Rendering ruby template '#{template_text_or_file}' from '#{tilt_template.file}' with locals:#{locals}, template_engine:#{template_engine}, Tilt.current_tempate: '#{Tilt.current_template}'"
50
+ tilt_template.render(self, **locals)
51
+ else
52
+ #puts " Render_ruby_template bypassing rendering for '#{template_text_or_file}', since '#{tilt_template}' is not a Tilt::Template"
53
+ tilt_template
54
+ end
55
+
56
+ #puts "RENDER_ruby_template '#{tilt_template}' result: #{rslt}"
57
+ rslt
58
+ end
59
+
60
+ def load_template(template_text_or_file, template_engine:nil)
61
+ #puts " LOADING template '#{template_text_or_file}' with engine: #{template_engine}"
62
+ current_eoutvar = Thread.current.instance_variable_get(:@current_eoutvar)
63
+ case template_text_or_file
64
+ when Tilt::Template
65
+ #puts " Loading existing tilt template '#{template_text_or_file}' from '#{template_text_or_file.file}' with engine: #{template_engine}"
66
+ template_text_or_file
67
+ when Symbol
68
+ #puts " Loading template from symbol '#{template_text_or_file}' with engine: #{template_engine}"
69
+ path = template_path(template_text_or_file, template_engine:template_engine)
70
+ #puts "RENDER_ruby_template path-if-symbol: #{path}"
71
+ if File.file?(path.to_s)
72
+ Tilt.new(path, 1, outvar: Vue::Helpers.vue_outvar)
73
+ else
74
+ # TODO: This should be logger.debug
75
+ #puts "RENDER_ruby_template template-missing: #{template_text_or_file}"
76
+ end
77
+ when String
78
+ #puts " Loading template from string '#{template_text_or_file}' with engine: #{template_engine}"
79
+ Tilt.template_for(template_engine || current_template_engine).new(nil, 1, outvar: Vue::Helpers.vue_outvar){template_text_or_file}
80
+ end
81
+ rescue
82
+ # TODO: Make this a logger.debug output.
83
+ puts "Render_ruby_template error building tilt template for #{template_text_or_file.to_s[0..32].gsub(/\n/, ' ')}...: #{$!}"
84
+ puts "BACKTRACE:"
85
+ puts $!.backtrace
86
+ nil
87
+ ensure
88
+ Thread.current.instance_variable_set(:@current_eoutvar, current_eoutvar)
89
+ end
90
+
91
+ # Returns nil instead of default if no current engine.
92
+ def current_template_engine(use_default=false)
93
+ #current_engine || Vue::Helpers.template_engine
94
+ Tilt.default_mapping.template_map.invert[Tilt.current_template.class] || (use_default && Vue::Helpers.template_engine)
95
+ end
96
+
97
+ # TODO: Decide how we want to determine template-engine suffix.
98
+ # Search for all possible suffixes? Search for only the given template_engine? Something else?
99
+ # Is this already handled here?
100
+ def template_path(name, template_engine:nil) #current_template_engine)
101
+ template_engine ||= '*'
102
+ #puts "TEMPLATE_path searching with name: #{name}, template_engine: #{template_engine}"
103
+ ([Vue::Helpers.views_path].flatten.uniq.compact || Dir.getwd).each do |start_path|
104
+ #puts "TEMPLATE_path searching views-path: #{start_path}"
105
+ Dir.breadth_first("*", base:start_path) do |path|
106
+ #puts "TEMPLATE_path inspecting file: #{path}"
107
+ return path if File.fnmatch(File.join('*', "#{name}.vue.#{template_engine}"), path)
108
+ return path if File.fnmatch(File.join('*', "#{name}.vue"), path)
109
+ return path if File.fnmatch(File.join('*', name.to_s), path)
110
+ end
111
+ end
112
+
113
+ return nil
114
+ end
115
+
116
+ # Capture & Concat
117
+ # See https://gist.github.com/seanami/496702
118
+ # TODO: These need to handle haml & slim as well.
119
+
120
+ def get_current_eoutvar
121
+ Thread.current.instance_variable_get(:@current_eoutvar)
122
+ end
123
+
124
+ # Returns any buffer (even if not defined).
125
+ def buffer(buffer_name = nil)
126
+ #@_out_buf
127
+ #buffer_name ||= Tilt.current_template.instance_variable_get('@outvar') || @outvar || Vue::Helpers.default_outvar
128
+ buffer_name ||=
129
+ ((ct = Tilt.current_template) && ct.instance_variable_get(:@outvar)) ||
130
+ Thread.current.instance_variable_get(:@current_eoutvar) ||
131
+ Vue::Helpers.default_outvar
132
+ #puts "BUFFER thread-ivars: #{Thread.current.instance_variables}, thread-local-vars: #{Thread.current.send(:local_variables)}"
133
+ #puts "BUFFER chosen: #{buffer_name}, controller/view ivars: #{instance_variables}, controller/view local-vars: #{local_variables}"
134
+ #instance_variable_get(buffer_name)
135
+ instance_variable_get(buffer_name.to_s)
136
+ end
137
+
138
+ # TODO: Probbably need to pass root_name (and other options?) on to sub-components inside block.
139
+ # Does vue even allow components in the block of a component call?
140
+ # TODO: Are *args and locals being used?
141
+ def capture_html(*args, root_name:Vue::Helpers.root_name, locals:{}, &block)
142
+ #puts "CAPTURE_HTML args: #{args}, root_name: #{root_name}, locals:#{locals}"
143
+ return unless block_given?
144
+
145
+ # This is mostly for Rails. Are there other frameworks that would use this?
146
+ return(capture(*args, &block)) if respond_to?(:capture)
147
+
148
+ # This is one of the points where we finally need to know what template
149
+ # we're using. If the actively-rendering template is not handled by Tilt,
150
+ # we can only take a best guess. If we're wrong, the user will need to set
151
+ # Vue::Helpers.defaults[:template_engine] to a known template type.
152
+ current_template = current_template_engine(true)
153
+ #puts "CAPTURE_HTML current_template: #{current_template}."
154
+ #puts "CAPTURE_HTML block info: block-local-vars:#{block.binding.local_variables}, block-ivars:#{block.binding.eval('instance_variables')}, controller-ivars:#{instance_variables}" if block_given?
155
+
156
+ case current_template.to_s
157
+ when /erb/i
158
+ #puts "Capturing ERB block with eoutvar '#{get_current_eoutvar}'."
159
+ #puts "Tilt @outvar '#{Tilt.current_template.instance_variable_get(:@outvar)}'"
160
+ #return(capture(*args, &block)) if respond_to?(:capture)
161
+ existing_buffer = buffer
162
+ if existing_buffer
163
+ pos = existing_buffer.to_s.size
164
+ yield(*args)
165
+ modified_buffer = buffer
166
+ modified_buffer.to_s.slice!(pos..modified_buffer.to_s.size)
167
+ else
168
+ yield(*args)
169
+ end
170
+ when /haml/i
171
+ #puts "Capturing HAML block."
172
+ capture_haml(*args, &block)
173
+ else
174
+ #puts "Capturing (yielding) to generic template block."
175
+ yield(*args)
176
+ end
177
+
178
+ end
179
+
180
+ def concat_content(text='')
181
+ return(text) if respond_to?(:capture)
182
+ current_template = current_template_engine(true)
183
+ #puts "CONCAT_CONTENT current_template: #{current_template}."
184
+ case current_template.to_s
185
+ when /erb/i
186
+ #puts "Concating to ERB outvar '#{get_current_eoutvar}'"
187
+ #puts "Tilt @outvar '#{Tilt.current_template.instance_variable_get(:@outvar)}'"
188
+ buffer.to_s << text
189
+ when /haml/i
190
+ haml_concat(text)
191
+ else
192
+ text
193
+ end
194
+ end
195
+
196
+ end # methods_block
197
+
198
+
199
+ if RUBY_VERSION.to_f < 2.4
200
+ # This needs to be defined anyway, since 'refine' is called
201
+ # in other modules/classes.
202
+ module HelperRefinements
203
+ end
204
+ # Use MethodsBlock as regular Module methods if Ruby < 2.4.
205
+ module Methods
206
+ class_eval(&MethodsBlock)
207
+ end
208
+ else
209
+ # Use MethodsBlock as Module refinements if Ruby >= 2.4.
210
+ module HelperRefinements
211
+ refine Methods do
212
+ MethodsBlock.call
213
+ end
214
+ end # HelperRefinements
215
+ using HelperRefinements
216
+ end
217
+
218
+ end # Helpers
219
+ end # Vue
@@ -0,0 +1,100 @@
1
+ require 'tilt'
2
+ require 'erb'
3
+
4
+ require_relative '../helpers'
5
+ require_relative 'helper_refinements'
6
+ require_relative 'utilities'
7
+ require_relative 'vue_repository'
8
+
9
+ module Vue
10
+ module Helpers
11
+ using CoreRefinements
12
+ using HelperRefinements
13
+
14
+ # Include this module in your controller (or action, or routes, or whatever).
15
+ #
16
+ # SEE helper_refinements.rb for the helpers supporting methods!
17
+ #
18
+ module Methods
19
+
20
+ def self.included(other)
21
+ # If Rails
22
+ if other.name[/Helper/] && Module.const_defined?(:ActionView)
23
+ # TODO: This should be logger.debug.
24
+ puts "#{other} including ActionView::Helpers::CaptureHelper"
25
+ include ActionView::Helpers::CaptureHelper
26
+ end
27
+
28
+ other.send(:prepend, ControllerPrepend)
29
+ end
30
+
31
+ def vue_repository
32
+ @vue_repository ||= VueRepository.new(context=self)
33
+ #puts "Getting vue_repository #{@vue_repository.class} with keys: #{@vue_repository.keys}"
34
+ @vue_repository
35
+ end
36
+ alias_method :vue_repo, :vue_repository
37
+
38
+ def vue_app(root_name=nil, **options)
39
+ vue_repository.root(root_name, **options)
40
+ end
41
+
42
+ # Inserts Vue component-call block in html template.
43
+ # Name & file_name refer to file-name.vue.<template_engine> SFC file. Example: products.vue.erb.
44
+ def vue_component(name,
45
+ root_name:nil,
46
+ tag_name:nil,
47
+ locals:{},
48
+ attributes:{},
49
+ **options,
50
+ &block
51
+ )
52
+
53
+ #puts "\nvue_component '#{name}' with local-vars '#{local_variables.inject({}){ |c, i| c[i.to_s] = eval(i.to_s); c }}'"
54
+
55
+ # This should only pass args that are necessary to build the component object.
56
+ # Tag-name and attributes are not relevant here.
57
+ component = vue_app(root_name).component(name, locals:locals, **options)
58
+
59
+ # Renders the per-call html block.
60
+ # Pass tag_name, attributes, locals, and block.
61
+ component_output = component.render(tag_name, locals:locals, attributes:attributes, &block)
62
+
63
+ # Concat the content if block given, otherwise just return the content.
64
+ if block_given?
65
+ #puts "Vue_component concating content for '#{name}'" #: #{component_output[0..32].gsub(/\n/, ' ')}"
66
+ concat_content(component_output)
67
+ else
68
+ #puts "Vue_component returning content for '#{name}'" #: #{component_output[0..32].gsub(/\n/, ' ')}"
69
+ return component_output
70
+ end
71
+ end # vue_component
72
+
73
+
74
+ # Inserts Vue app-call block in html template.
75
+ # Builds vue html and js for return to browser.
76
+ #
77
+ # Returns (or concats if block given) rendered html and js.
78
+ def vue_root(root_name = Vue::Helpers.root_name,
79
+ locals: {},
80
+ **options,
81
+ &block
82
+ )
83
+
84
+ #puts "\nvue_root '#{root_name}' with local-vars '#{local_variables.inject({}) { |c, i| c[i.to_s] = eval(i.to_s); c }}'"
85
+
86
+ root_app = vue_app(root_name, locals:locals, **options)
87
+
88
+ root_output = root_app.render(locals:locals, &block)
89
+
90
+ if block_given?
91
+ concat_content(root_output)
92
+ else
93
+ root_output
94
+ end
95
+ end
96
+
97
+
98
+ end # Methods
99
+ end # Helpers
100
+ end # Vue
@@ -0,0 +1,43 @@
1
+ require 'rack'
2
+ require_relative 'methods'
3
+ require_relative 'utilities'
4
+
5
+ module Vue
6
+ module Helpers
7
+ using CoreRefinements
8
+
9
+ # Rack middleware to serve sourced vue block, see https://redpanthers.co/rack-middleware/.
10
+ # Usage: use Vue::Helpers::Server
11
+
12
+ class Server
13
+ def initialize(app)
14
+ @app = app
15
+ end
16
+
17
+ def call(env)
18
+ req = Rack::Request.new(env)
19
+ case req.path_info
20
+ #when /^\/vuesource\/([A-Za-z0-9\-_]{43})$/
21
+ when /^#{Vue::Helpers.callback_prefix}\/([A-Za-z0-9\-_]{43})$/
22
+ #puts "vue_source match: #{$1}"
23
+ if content = get_content($1)
24
+ [200, {"Content-Type" => "text/javascript"}, [content]]
25
+ else
26
+ [404, {"Content-Type" => "text/html"}, ['']]
27
+ end
28
+ when /^\/pingm$/
29
+ [200, {"Content-Type" => "text/javascript"}, ['Ok']]
30
+ else
31
+ #[404, {"Content-Type" => "text/html"}, ["I'm Lost!"]]
32
+ @app.call(env)
33
+ end
34
+ end
35
+
36
+ def get_content(key)
37
+ Vue::Helpers.cache_store.delete(key)
38
+ #Vue::Helpers.cache_store[key]
39
+ end
40
+
41
+ end # Source
42
+ end # Helpers
43
+ end