├── .gitignore ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── circle.yml ├── lib ├── generators │ └── vue_melt │ │ ├── source │ │ ├── application.js │ │ ├── components │ │ │ └── Hello.vue │ │ ├── options │ │ │ └── users.js │ │ └── store │ │ │ ├── actions.js │ │ │ ├── getters.js │ │ │ ├── index.js │ │ │ ├── mutation-types.js │ │ │ └── mutations.js │ │ └── vue_melt_generator.rb ├── rails_vue_melt.rb └── rails_vue_melt │ └── version.rb ├── rails_vue_melt.gemspec └── spec ├── fixtures ├── application.html.erb └── environment.js └── vue_melt_generator_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | log/*.log 3 | pkg/ 4 | 5 | node_modules/ 6 | package.json 7 | yarn.lock 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Declare your gem's dependencies in rails_vue_melt.gemspec. 4 | # Bundler will treat runtime dependencies like base dependencies, and 5 | # development dependencies will be added by default to the :development group. 6 | gemspec 7 | 8 | # Declare any dependencies that are still in development here instead of in 9 | # your gemspec. These might include edge Rails or gems from your path or 10 | # Git. Remember to move these dependencies to your gemspec before releasing 11 | # your gem to rubygems.org. 12 | 13 | # To use a debugger 14 | # gem 'byebug', group: [:development, :test] 15 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | rails_vue_melt (0.2.0) 5 | rails (~> 5.1) 6 | webpacker (~> 3.0) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actioncable (5.1.2) 12 | actionpack (= 5.1.2) 13 | nio4r (~> 2.0) 14 | websocket-driver (~> 0.6.1) 15 | actionmailer (5.1.2) 16 | actionpack (= 5.1.2) 17 | actionview (= 5.1.2) 18 | activejob (= 5.1.2) 19 | mail (~> 2.5, >= 2.5.4) 20 | rails-dom-testing (~> 2.0) 21 | actionpack (5.1.2) 22 | actionview (= 5.1.2) 23 | activesupport (= 5.1.2) 24 | rack (~> 2.0) 25 | rack-test (~> 0.6.3) 26 | rails-dom-testing (~> 2.0) 27 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 28 | actionview (5.1.2) 29 | activesupport (= 5.1.2) 30 | builder (~> 3.1) 31 | erubi (~> 1.4) 32 | rails-dom-testing (~> 2.0) 33 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 34 | activejob (5.1.2) 35 | activesupport (= 5.1.2) 36 | globalid (>= 0.3.6) 37 | activemodel (5.1.2) 38 | activesupport (= 5.1.2) 39 | activerecord (5.1.2) 40 | activemodel (= 5.1.2) 41 | activesupport (= 5.1.2) 42 | arel (~> 8.0) 43 | activesupport (5.1.2) 44 | concurrent-ruby (~> 1.0, >= 1.0.2) 45 | i18n (~> 0.7) 46 | minitest (~> 5.1) 47 | tzinfo (~> 1.1) 48 | arel (8.0.0) 49 | builder (3.2.3) 50 | concurrent-ruby (1.0.5) 51 | diff-lcs (1.3) 52 | erubi (1.6.1) 53 | generator_spec (0.9.4) 54 | activesupport (>= 3.0.0) 55 | railties (>= 3.0.0) 56 | globalid (0.4.0) 57 | activesupport (>= 4.2.0) 58 | i18n (0.8.6) 59 | loofah (2.0.3) 60 | nokogiri (>= 1.5.9) 61 | mail (2.6.6) 62 | mime-types (>= 1.16, < 4) 63 | method_source (0.8.2) 64 | mime-types (3.1) 65 | mime-types-data (~> 3.2015) 66 | mime-types-data (3.2016.0521) 67 | mini_portile2 (2.2.0) 68 | minitest (5.10.3) 69 | nio4r (2.1.0) 70 | nokogiri (1.8.0) 71 | mini_portile2 (~> 2.2.0) 72 | rack (2.0.3) 73 | rack-proxy (0.6.2) 74 | rack 75 | rack-test (0.6.3) 76 | rack (>= 1.0) 77 | rails (5.1.2) 78 | actioncable (= 5.1.2) 79 | actionmailer (= 5.1.2) 80 | actionpack (= 5.1.2) 81 | actionview (= 5.1.2) 82 | activejob (= 5.1.2) 83 | activemodel (= 5.1.2) 84 | activerecord (= 5.1.2) 85 | activesupport (= 5.1.2) 86 | bundler (>= 1.3.0, < 2.0) 87 | railties (= 5.1.2) 88 | sprockets-rails (>= 2.0.0) 89 | rails-dom-testing (2.0.3) 90 | activesupport (>= 4.2.0) 91 | nokogiri (>= 1.6) 92 | rails-html-sanitizer (1.0.3) 93 | loofah (~> 2.0) 94 | railties (5.1.2) 95 | actionpack (= 5.1.2) 96 | activesupport (= 5.1.2) 97 | method_source 98 | rake (>= 0.8.7) 99 | thor (>= 0.18.1, < 2.0) 100 | rake (12.0.0) 101 | rspec (3.6.0) 102 | rspec-core (~> 3.6.0) 103 | rspec-expectations (~> 3.6.0) 104 | rspec-mocks (~> 3.6.0) 105 | rspec-core (3.6.0) 106 | rspec-support (~> 3.6.0) 107 | rspec-expectations (3.6.0) 108 | diff-lcs (>= 1.2.0, < 2.0) 109 | rspec-support (~> 3.6.0) 110 | rspec-mocks (3.6.0) 111 | diff-lcs (>= 1.2.0, < 2.0) 112 | rspec-support (~> 3.6.0) 113 | rspec-support (3.6.0) 114 | sprockets (3.7.1) 115 | concurrent-ruby (~> 1.0) 116 | rack (> 1, < 3) 117 | sprockets-rails (3.2.1) 118 | actionpack (>= 4.0) 119 | activesupport (>= 4.0) 120 | sprockets (>= 3.0.0) 121 | thor (0.19.4) 122 | thread_safe (0.3.6) 123 | tzinfo (1.2.3) 124 | thread_safe (~> 0.1) 125 | webpacker (3.0.1) 126 | activesupport (>= 4.2) 127 | rack-proxy (>= 0.6.1) 128 | railties (>= 4.2) 129 | websocket-driver (0.6.5) 130 | websocket-extensions (>= 0.1.0) 131 | websocket-extensions (0.1.2) 132 | 133 | PLATFORMS 134 | ruby 135 | 136 | DEPENDENCIES 137 | generator_spec 138 | rails_vue_melt! 139 | rspec 140 | 141 | BUNDLED WITH 142 | 1.14.2 143 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 midnightSuyama 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RailsVueMelt 2 | 3 | [![Gem Version](https://badge.fury.io/rb/rails_vue_melt.svg)](http://badge.fury.io/rb/rails_vue_melt) 4 | [![CircleCI](https://circleci.com/gh/midnightSuyama/rails_vue_melt.svg?style=shield)](https://circleci.com/gh/midnightSuyama/rails_vue_melt) 5 | 6 | Rails view with webpack=vue optimizer 7 | 8 | ## Installation 9 | 10 | Add this line to your application's Gemfile: 11 | 12 | ```ruby 13 | gem 'rails_vue_melt' 14 | ``` 15 | 16 | And then execute: 17 | 18 | $ bundle 19 | 20 | Or install it yourself as: 21 | 22 | $ gem install rails_vue_melt 23 | 24 | ## Usage 25 | 26 | $ rails new APP_PATH --webpack=vue 27 | ... 28 | $ rails generate vue_melt 29 | 30 | ### Generate 31 | 32 | #### create `app/javascript/packs/vue_melt` 33 | 34 | * application.js 35 | * options/ 36 | * users.js 37 | * components/ 38 | * Hello.vue 39 | * store/ 40 | * index.js 41 | * getters.js 42 | * actions.js 43 | * mutations.js 44 | * mutation-types.js 45 | 46 | #### insert `app/views/layouts/application.html.erb` 47 | 48 | ```html 49 | <%= javascript_pack_tag 'vue_melt/application' %> 50 | 51 | ``` 52 | 53 | #### insert `config/webpack/environment.js` 54 | 55 | ```javascript 56 | environment.loaders.get('vue').options.extractCSS = false 57 | ``` 58 | 59 | #### install packages 60 | 61 | * [vuex](https://www.npmjs.com/package/vuex) 62 | * [vue-assign-model](https://www.npmjs.com/package/vue-assign-model) 63 | * [lodash.clonedeep](https://www.npmjs.com/package/lodash.clonedeep) 64 | 65 | ### Example 66 | 67 | ```erb 68 | 69 | 70 |
71 | 72 |

User Name: {{ user.name }}

73 | 74 | 75 |
76 | ``` 77 | 78 | Add `data-vue` attribute, the value is file name in `app/javascript/packs/vue_melt/options`. Vue instance is mounted at `turbolinks:load` event and unmounted at `turbolinks:visit` event. 79 | 80 | Before Vue rendering, `value`, `checked` or `selected` attributes of elements with `v-model` is automatically assigned to Vue model. Therefore, `form_with` and so on using Active Model can use data binding easily. 81 | 82 | #### accepts_nested_attributes_for 83 | 84 | JSON of `data-vue-model` is assigned to Vue model. Also, `v-model` with prefix `_` is not assigned for `v-for` scope. 85 | 86 | ```erb 87 | 88 | 89 |
90 | <%= content_tag :div, 'data-vue-model': "{ \"items\": #{user.items.to_json} }" do %> 91 |
92 | 93 | 94 |
95 | <% end %> 96 |
97 | ``` 98 | 99 | With Add and Remove function: 100 | 101 | ```erb 102 | 103 | 104 |
105 | <%= content_tag :div, 'data-vue-model': "{ \"items\": #{user.items.to_json}, \"items_destroy_ids\": [] }" do %> 106 |
107 | 108 | 109 | 110 |
111 | 112 | 116 | 117 | 118 | <% end %> 119 |
120 | ``` 121 | 122 | ## Development 123 | 124 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 125 | 126 | ## Contributing 127 | 128 | Bug reports and pull requests are welcome on GitHub at https://github.com/midnightSuyama/rails_vue_melt. 129 | 130 | ## License 131 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 132 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/setup' 3 | rescue LoadError 4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 5 | end 6 | 7 | require 'rdoc/task' 8 | 9 | RDoc::Task.new(:rdoc) do |rdoc| 10 | rdoc.rdoc_dir = 'rdoc' 11 | rdoc.title = 'RailsVueMelt' 12 | rdoc.options << '--line-numbers' 13 | rdoc.rdoc_files.include('README.md') 14 | rdoc.rdoc_files.include('lib/**/*.rb') 15 | end 16 | 17 | 18 | 19 | 20 | 21 | 22 | require 'bundler/gem_tasks' 23 | 24 | require 'rspec/core/rake_task' 25 | 26 | RSpec::Core::RakeTask.new(:spec) 27 | 28 | task default: :spec 29 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | ruby: 3 | version: 2.3.4 4 | 5 | test: 6 | override: 7 | - bundle exec rspec 8 | -------------------------------------------------------------------------------- /lib/generators/vue_melt/source/application.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue/dist/vue.esm' 2 | import store from './store' 3 | import cloneDeep from 'lodash.clonedeep' 4 | 5 | import VueAssignModel from 'vue-assign-model' 6 | Vue.use(VueAssignModel) 7 | 8 | const storeState = cloneDeep(store.state) 9 | var vms = [] 10 | var options = {} 11 | var requireContext = require.context("./options", false, /\.js$/) 12 | requireContext.keys().forEach(key => { 13 | let name = key.split('/').pop().split('.').shift() 14 | options[name] = requireContext(key).default 15 | }) 16 | 17 | document.addEventListener('turbolinks:load', () => { 18 | let templates = document.querySelectorAll('[data-vue]') 19 | for (let el of templates) { 20 | let vm = new Vue( 21 | Object.assign(options[el.dataset.vue], { el, store }) 22 | ) 23 | vms.push(vm) 24 | } 25 | }) 26 | 27 | document.addEventListener('turbolinks:visit', () => { 28 | for (let vm of vms) { 29 | vm.$destroy() 30 | } 31 | vms = [] 32 | store.replaceState(cloneDeep(storeState)) 33 | }) 34 | -------------------------------------------------------------------------------- /lib/generators/vue_melt/source/components/Hello.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 21 | -------------------------------------------------------------------------------- /lib/generators/vue_melt/source/options/users.js: -------------------------------------------------------------------------------- 1 | import Hello from '../components/Hello.vue' 2 | 3 | export default { 4 | components: { 5 | Hello 6 | }, 7 | data: () => ({ 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /lib/generators/vue_melt/source/store/actions.js: -------------------------------------------------------------------------------- 1 | import * as types from './mutation-types' 2 | -------------------------------------------------------------------------------- /lib/generators/vue_melt/source/store/getters.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/midnightSuyama/rails_vue_melt/a9c3d15b3064992e04510514a15546124331ce5d/lib/generators/vue_melt/source/store/getters.js -------------------------------------------------------------------------------- /lib/generators/vue_melt/source/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue/dist/vue.esm' 2 | import Vuex from 'vuex' 3 | import * as getters from './getters' 4 | import * as actions from './actions' 5 | import * as mutations from './mutations' 6 | 7 | Vue.use(Vuex) 8 | 9 | const state = { 10 | } 11 | 12 | export default new Vuex.Store({ 13 | state, 14 | getters, 15 | actions, 16 | mutations 17 | }) 18 | -------------------------------------------------------------------------------- /lib/generators/vue_melt/source/store/mutation-types.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/midnightSuyama/rails_vue_melt/a9c3d15b3064992e04510514a15546124331ce5d/lib/generators/vue_melt/source/store/mutation-types.js -------------------------------------------------------------------------------- /lib/generators/vue_melt/source/store/mutations.js: -------------------------------------------------------------------------------- 1 | import * as types from './mutation-types' 2 | -------------------------------------------------------------------------------- /lib/generators/vue_melt/vue_melt_generator.rb: -------------------------------------------------------------------------------- 1 | class VueMeltGenerator < Rails::Generators::Base 2 | source_root File.expand_path('../source', __FILE__) 3 | 4 | def create 5 | directory '.', 'app/javascript/packs/vue_melt' 6 | end 7 | 8 | def edit 9 | inject_into_file 'app/views/layouts/application.html.erb', before: /^\s*<\/head>/ do 10 | <<-EOS 11 | <%= javascript_pack_tag 'vue_melt/application' %> 12 | 13 | EOS 14 | end 15 | 16 | inject_into_file 'config/webpack/environment.js', before: /^module\.exports = environment$/ do 17 | <<-EOS 18 | environment.loaders.get('vue').options.extractCSS = false 19 | EOS 20 | end 21 | end 22 | 23 | def yarn 24 | run 'yarn add vuex vue-assign-model lodash.clonedeep' 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/rails_vue_melt.rb: -------------------------------------------------------------------------------- 1 | module RailsVueMelt 2 | end 3 | -------------------------------------------------------------------------------- /lib/rails_vue_melt/version.rb: -------------------------------------------------------------------------------- 1 | module RailsVueMelt 2 | VERSION = '0.2.0' 3 | end 4 | -------------------------------------------------------------------------------- /rails_vue_melt.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | 3 | # Maintain your gem's version: 4 | require "rails_vue_melt/version" 5 | 6 | # Describe your gem and declare its dependencies: 7 | Gem::Specification.new do |s| 8 | s.name = "rails_vue_melt" 9 | s.version = RailsVueMelt::VERSION 10 | s.authors = ["midnightSuyama"] 11 | s.email = ["midnightSuyama@gmail.com"] 12 | s.homepage = "https://github.com/midnightSuyama/rails_vue_melt" 13 | s.summary = "Rails view with webpack=vue optimizer" 14 | s.description = "Rails view with webpack=vue optimizer" 15 | s.license = "MIT" 16 | 17 | s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] 18 | 19 | s.add_dependency "rails", "~> 5.1" 20 | s.add_dependency "webpacker", "~> 3.0" 21 | 22 | s.add_development_dependency "rspec" 23 | s.add_development_dependency "generator_spec" 24 | end 25 | -------------------------------------------------------------------------------- /spec/fixtures/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test 5 | <%= csrf_meta_tags %> 6 | 7 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> 8 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> 9 | 10 | 11 | 12 | <%= yield %> 13 | 14 | 15 | -------------------------------------------------------------------------------- /spec/fixtures/environment.js: -------------------------------------------------------------------------------- 1 | const { environment } = require('@rails/webpacker') 2 | 3 | module.exports = environment 4 | -------------------------------------------------------------------------------- /spec/vue_melt_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'generator_spec' 2 | require File.expand_path('../../lib/generators/vue_melt/vue_melt_generator', __FILE__) 3 | 4 | describe VueMeltGenerator, type: :generator do 5 | destination File.expand_path("../tmp", __FILE__) 6 | 7 | before :all do 8 | prepare_destination 9 | mkdir_p "#{destination_root}/app/views/layouts" 10 | cp File.expand_path('fixtures/application.html.erb', File.dirname(__FILE__)), "#{destination_root}/app/views/layouts" 11 | mkdir_p "#{destination_root}/config/webpack" 12 | cp File.expand_path('fixtures/environment.js', File.dirname(__FILE__)), "#{destination_root}/config/webpack" 13 | run_generator 14 | end 15 | 16 | after :all do 17 | rm_rf destination_root 18 | system 'yarn remove vuex vue-assign-model lodash.clonedeep > /dev/null' 19 | end 20 | 21 | it 'creates vue_melt' do 22 | assert_file 'app/javascript/packs/vue_melt/application.js' 23 | assert_file 'app/javascript/packs/vue_melt/options' 24 | assert_file 'app/javascript/packs/vue_melt/components' 25 | assert_file 'app/javascript/packs/vue_melt/store' 26 | end 27 | 28 | it 'inserts javascript_pack_tag and turbolinks-cache-control' do 29 | content = File.read("#{destination_root}/app/views/layouts/application.html.erb") 30 | expect(content).to match(/\s*<%= javascript_pack_tag 'vue_melt\/application' %>\n\s*\n\s*<\/head>/) 31 | end 32 | 33 | it 'changes extractCSS' do 34 | content = File.read("#{destination_root}/config/webpack/environment.js") 35 | expect(content).to match(/environment\.loaders\.get\('vue'\)\.options\.extractCSS = false\nmodule\.exports = environment$/) 36 | end 37 | 38 | it 'installs package' do 39 | assert_file '../../node_modules/vuex' 40 | assert_file '../../node_modules/vue-assign-model' 41 | assert_file '../../node_modules/lodash.clonedeep' 42 | end 43 | end 44 | --------------------------------------------------------------------------------