vue_rails 0.1.1

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.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "vue_rails"
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
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -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,31 @@
1
+ require 'rails/generators'
2
+
3
+ module Vue
4
+ module Generators
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+ desc "add vue_ujs to javascripts/packs"
7
+ source_root File.expand_path('../../../../', __FILE__)
8
+
9
+ def add_initializer
10
+ javascript_packs_dir = ::Webpacker.config.source_entry_path
11
+ javascript_dir = javascript_packs_dir.parent
12
+ template "vue_ujs/vue_ujs.js", "#{javascript_dir}/rails_vue_ujs.js"
13
+ create_file "#{javascript_packs_dir}/vue_server_render.js"
14
+
15
+ setup_js = <<-JS
16
+ import RailsVueUJS from '../rails_vue_ujs';
17
+ var componentRequireContext = require.context("vue_components", true);
18
+ RailsVueUJS.initialComponentsContext(componentRequireContext);
19
+ self.RailsVueUJS = RailsVueUJS;
20
+ JS
21
+ append_file "#{javascript_packs_dir}/vue_server_render.js", setup_js
22
+ append_file "#{javascript_packs_dir}/application.js", setup_js
23
+ empty_directory "#{javascript_dir}/vue_components"
24
+ remove_file "#{javascript_dir}/app.vue"
25
+ remove_file "#{javascript_packs_dir}/hello_vue.js"
26
+ template "vue_ujs/hello.vue", "#{javascript_dir}/vue_components/hello.vue"
27
+ `yarn add vue-server-renderer`
28
+ end
29
+ end
30
+ end
31
+ end
data/lib/vue_rails.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "vue_rails/version"
2
+ require "vue_rails/renderer"
3
+ require "vue_rails/engine"
4
+
5
+ module VueRails
6
+ class Error < StandardError; end
7
+ # Your code goes here...
8
+ end
@@ -0,0 +1,99 @@
1
+ require 'open-uri'
2
+
3
+ module VueRails
4
+ class WebpackerAssetFinder
5
+ begin
6
+ MAJOR, MINOR, PATCH, _ = Bundler.locked_gems.specs.find { |gem_spec| gem_spec.name == 'webpacker' }.version.segments
7
+ rescue
8
+ MAJOR, MINOR, PATCH, _ = [0,0,0]
9
+ end
10
+
11
+ # This pattern matches the code that initializes the dev-server client.
12
+ CLIENT_REQUIRE = %r{__webpack_require__\(.*webpack-dev-server\/client\/index\.js.*\n}
13
+
14
+ def self.compatible?
15
+ !!defined?(Webpacker)
16
+ end
17
+
18
+ if MAJOR < 3
19
+ def find_path(logical_path)
20
+ asset_path = manifest.lookup(logical_path).to_s
21
+ asset_path.start_with?('http') ? asset_path : file_path(logical_path).to_s
22
+ end
23
+
24
+ def find_asset(logical_path)
25
+ # raises if not found
26
+ asset_path = manifest.lookup(logical_path).to_s
27
+ if asset_path.start_with?('http')
28
+ # Get a file from the webpack-dev-server
29
+ dev_server_asset = open(asset_path).read
30
+ # Remove `webpack-dev-server/client/index.js` code which causes ExecJS to 💥
31
+ dev_server_asset.sub!(CLIENT_REQUIRE, '//\0')
32
+ dev_server_asset
33
+ else
34
+ # Read the already-compiled pack:
35
+ full_path = file_path(logical_path).to_s
36
+ File.read(full_path)
37
+ end
38
+ end
39
+ else
40
+ def find_path(logical_path)
41
+ asset_path = Webpacker.manifest.lookup(logical_path).to_s
42
+ Webpacker.dev_server.running? ? asset_path : file_path(logical_path)
43
+ end
44
+
45
+ def find_asset(logical_path)
46
+ asset_path = Webpacker.manifest.lookup(logical_path).to_s
47
+ if Webpacker.dev_server.running?
48
+ ds = Webpacker.dev_server
49
+ # Remove the protocol and host from the asset path. Sometimes webpacker includes this, sometimes it does not
50
+ asset_path.slice!("#{ds.protocol}://#{ds.host_with_port}")
51
+ dev_server_asset = open("#{ds.protocol}://#{ds.host_with_port}#{asset_path}").read
52
+ dev_server_asset.sub!(CLIENT_REQUIRE, '//\0')
53
+ dev_server_asset
54
+ else
55
+ File.read(file_path(logical_path))
56
+ end
57
+ end
58
+ end
59
+
60
+ if MAJOR < 3
61
+ def manifest
62
+ Webpacker::Manifest
63
+ end
64
+ else
65
+ def manifest
66
+ Webpacker.manifest
67
+ end
68
+ end
69
+
70
+ if MAJOR < 3
71
+ def config
72
+ Webpacker::Configuration
73
+ end
74
+ else
75
+ def config
76
+ Webpacker.config
77
+ end
78
+ end
79
+
80
+ if (MAJOR == 1 && MINOR >= 2) || MAJOR == 2
81
+ def file_path path
82
+ manifest.lookup_path(path)
83
+ end
84
+ elsif MAJOR == 3
85
+ def file_path path
86
+ ::Rails.root.join('public', manifest.lookup(path)[1..-1])
87
+ end
88
+ else # 1.0 and 1.1 support
89
+ def file_path path
90
+ File.join(output_path, manifest.lookup(path).split('/')[2..-1])
91
+ end
92
+ end
93
+
94
+ def output_path
95
+ # Webpack1 /:output/:entry, Webpack3 /public/:output
96
+ config.respond_to?(:output_path) ? config.output_path : 'public'
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,11 @@
1
+ require "vue_rails/view_helper"
2
+
3
+ module VueRails
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace VueRails
6
+
7
+ initializer "vue_rails.engine" do
8
+ ActionView::Base.send :include, VueRails::ViewHelper
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,56 @@
1
+ require 'vue_rails/asset_finder'
2
+
3
+ module VueRails
4
+ class Renderer
5
+ GLOBAL_WRAPPER = <<-JS
6
+ var global = global || this;
7
+ var self = self || this;
8
+ JS
9
+
10
+ CONSOLE_POLYFILL = <<-JS
11
+ var console = { history: [] };
12
+ ['error', 'log', 'info', 'warn'].forEach(function (fn) {
13
+ console[fn] = function () {
14
+ console.history.push({level: fn, arguments: Array.prototype.slice.call(arguments)});
15
+ };
16
+ });
17
+ JS
18
+
19
+ cattr_accessor :context
20
+ attr_accessor :component, :props, :router_push_to, :state
21
+
22
+ def initialize component, props, router_push_to=nil, state={}
23
+ self.component = component
24
+ self.props = props
25
+ self.router_push_to = router_push_to
26
+ self.state = state
27
+ end
28
+
29
+ def self.server_render *args
30
+ new(*args).render
31
+ end
32
+
33
+ def render
34
+ if Rails.env.production?
35
+ self.context ||= (
36
+ js_code = VueRails::WebpackerAssetFinder.new.find_asset("vue_server_render.js")
37
+ ExecJS.compile(GLOBAL_WRAPPER + CONSOLE_POLYFILL + js_code)
38
+ )
39
+ else
40
+ self.context = (
41
+ js_code = VueRails::WebpackerAssetFinder.new.find_asset("vue_server_render.js")
42
+ ExecJS.compile(GLOBAL_WRAPPER + CONSOLE_POLYFILL + js_code)
43
+ )
44
+ end
45
+
46
+ self.context.eval("RailsVueUJS.serverRender('#{component}', #{props}, '#{router_push_to}', #{state})")
47
+ end
48
+
49
+ private
50
+
51
+ def cache_key
52
+ path = VueRails::WebpackerAssetFinder.new.find_path("vue_server_render.js")
53
+ Digest::MD5.hexdigest("#{component}_#{JSON.parse(props).delete_if {|k, _| k.to_s == "csrf_token"}}_#{router_push_to}_#{path}_#{state}")
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module VueRails
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,14 @@
1
+ module VueRails
2
+ module ViewHelper
3
+ def vue_component(component_name, data={}, options={})
4
+ data[:csrf_token] = form_authenticity_token
5
+ options.merge!(name: component_name, __is_vue_component__: true)
6
+ prerender = options.delete(:prerender)
7
+ state = (options.delete(:state) || {}).to_json
8
+ router_push_to = prerender && prerender.is_a?(String) ? prerender : nil
9
+ content = prerender ?
10
+ VueRails::Renderer.server_render(component_name, data.to_json, router_push_to, state)&.html_safe : nil
11
+ content_tag(:div, content, options.merge(data: {vue_data: data.to_json, vue_state: state}))
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "vue_rails/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "vue_rails"
8
+ spec.version = VueRails::VERSION
9
+ spec.authors = ["纪亚荣"]
10
+ spec.email = ["583255925@qq.com"]
11
+
12
+ spec.summary = 'Vue integration for Ruby on Rails'
13
+ spec.description = 'initialize Vue Components in Rails Views and server render'
14
+ spec.homepage = "https://github.com/jiyarong/vue_rails"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
18
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib", "vue_ujs"]
23
+
24
+ spec.add_development_dependency 'bundler', '>= 1.2.2'
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest", "~> 5.0"
27
+ spec.add_development_dependency 'rails', '>= 5.1'
28
+ spec.add_development_dependency 'webpacker'
29
+ spec.add_dependency 'execjs'
30
+ end
data/vue_ujs/hello.vue ADDED
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <div id="app">
3
+ <p>{{ message }}</p>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ data: function () {
10
+ return {
11
+ message: "Hello Vue!"
12
+ }
13
+ }
14
+ }
15
+ </script>
16
+
17
+ <style scoped>
18
+ p {
19
+ font-size: 2em;
20
+ text-align: center;
21
+ }
22
+ </style>
@@ -0,0 +1,147 @@
1
+ import Vue from 'vue/dist/vue.esm.js'
2
+ var renderVueComponentToString = require("vue-server-renderer/basic.js");
3
+
4
+ self.Vue = Vue;
5
+
6
+ var RailsVueUJS = {
7
+ initialComponentsContext: function (context) {
8
+ self.RailsVueUJS.getConstructorByName = function (componentName) {
9
+ return context(`./${componentName}`).default;
10
+ }
11
+ },
12
+
13
+ handleVueDestructionOn: function(turbolinksEvent, vue) {
14
+ document.addEventListener(turbolinksEvent, function teardown() {
15
+ vue.$destroy();
16
+ document.removeEventListener(turbolinksEvent, teardown);
17
+ });
18
+ },
19
+
20
+ TurbolinksAdapter: function(Vue) {
21
+ Vue.mixin({
22
+ beforeMount: function() {
23
+ // If this is the root component, we want to cache the original element contents to replace later
24
+ // We don't care about sub-components, just the root
25
+ if (this == this.$root && this.$el) {
26
+ var destroyEvent = this.$options.turbolinksDestroyEvent || 'turbolinks:visit';
27
+ RailsVueUJS.handleVueDestructionOn(destroyEvent, this);
28
+ this.$originalEl = this.$el.outerHTML;
29
+ }
30
+ },
31
+
32
+ destroyed: function() {
33
+ // We only need to revert the html for the root component
34
+ if (this == this.$root && this.$el) {
35
+ this.$el.outerHTML = this.$originalEl;
36
+ }
37
+ }
38
+ })
39
+ },
40
+
41
+ use: function (...ms) {
42
+ ms.forEach((m) => {
43
+ self.Vue.use(m);
44
+ })
45
+ },
46
+
47
+ initializeVuexStore: function (store) {
48
+ RailsVueUJS.store = store
49
+ },
50
+
51
+ serverRender: function (componentName, props={}, router_to=null, initialState={}) {
52
+ let component = self.RailsVueUJS.getConstructorByName(componentName);
53
+ let componentInitialName = componentName.split("/").pop();
54
+ let componentsArgs = {};
55
+ componentsArgs[componentInitialName] = component;
56
+ let initializeObject = {
57
+ data: {data: props},
58
+ template: `<${componentInitialName} :outside="data" :env_ssr="true" />`,
59
+ components: componentsArgs
60
+ };
61
+
62
+ if (RailsVueUJS.store !== undefined) {
63
+ let currentState = RailsVueUJS.store.state;
64
+ Object.keys(initialState).forEach((k) => {
65
+ currentState[k] = initialState[k]
66
+ });
67
+
68
+ RailsVueUJS.store.replaceState(currentState);
69
+
70
+ initializeObject.store = RailsVueUJS.store
71
+ }
72
+
73
+ let v = new self.Vue(initializeObject);
74
+
75
+ if (component.router && router_to !== null)
76
+ component.router.push(router_to);
77
+
78
+ let str = "";
79
+
80
+ renderVueComponentToString(v, (err, res) => {
81
+ str = res
82
+ });
83
+
84
+ (function (history) {
85
+ if (history && history.length > 0) {
86
+ str += '\n<scr'+'ipt>';
87
+ history.forEach(function (msg) {
88
+ str += '\nconsole.' + msg.level + '.apply(console, ' + JSON.stringify(msg.arguments) + ');';
89
+ });
90
+ str += '\n</scr'+'ipt>';
91
+ }
92
+ })(console.history);
93
+
94
+ return str
95
+ },
96
+
97
+ detectComponents: function () {
98
+ document.querySelectorAll("[__is_vue_component__=true]").forEach((d) => {
99
+ let componentName = d.getAttribute('name');
100
+ let vueComponent = self.RailsVueUJS.getConstructorByName(componentName);
101
+ let componentInitialName = componentName.split("/").pop();
102
+ let componentsArgs = {};
103
+ componentsArgs[componentInitialName] = vueComponent;
104
+
105
+ let initializeObject = {
106
+ el: d,
107
+ data: {data: JSON.parse(d.dataset.vueData)},
108
+ template: `<${componentInitialName} :outside="data" :env_ssr="false" />`,
109
+ components: componentsArgs
110
+ };
111
+
112
+ if (RailsVueUJS.store !== undefined) {
113
+ let currentState = RailsVueUJS.store.state;
114
+ let replaceState = JSON.parse(d.dataset.vueState);
115
+ Object.keys(replaceState).forEach((k) => {
116
+ currentState[k] = replaceState[k]
117
+ });
118
+
119
+ RailsVueUJS.store.replaceState(currentState);
120
+ initializeObject.store = RailsVueUJS.store
121
+ }
122
+
123
+ new Vue(initializeObject)
124
+ })
125
+ },
126
+
127
+ detectEvents: function () {
128
+ self.addEventListener('load', function () {
129
+ let TurbolinksDetected = 'Turbolinks' in window && Turbolinks.supported;
130
+ if (TurbolinksDetected) {
131
+ RailsVueUJS.use(RailsVueUJS.TurbolinksAdapter);
132
+
133
+ self.addEventListener('turbolinks:load', function () {
134
+ RailsVueUJS.detectComponents();
135
+ })
136
+ }
137
+ RailsVueUJS.detectComponents();
138
+ });
139
+ }
140
+ };
141
+
142
+ if (typeof window !== "undefined") {
143
+ RailsVueUJS.detectEvents();
144
+ }
145
+
146
+ self.RailsVueUJS = RailsVueUJS;
147
+ export default RailsVueUJS;