neutron-ruby-electron 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7b94b9dba9d64ca5279b5b46edaaa489678eb8fd
4
+ data.tar.gz: 15f706d8e6bd8744117795b0af8bacf34fadc4c2
5
+ SHA512:
6
+ metadata.gz: 4710ac1b334f0bc28a47bf4c62a266b34016bf1a21b90e127e0a5542e1dbee4dcee2c3d14f3b48337faefc854f06ed07766fdb9c334a20fd8d094fb12ed52dc8
7
+ data.tar.gz: ffc603482d038a546562ad1983353b899303074947277778aec85969e8534b5517d64863d97767807eb53f0df2bdd80fcb5166984e3b38e68fa4b7e10d24abab
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /local_test/
10
+ /tmp/
11
+
12
+ /ciccio/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in neutron.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 pioz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # Neutron
2
+
3
+ Neutron is a mini framework to build desktop app with [Electron](https://electron.atom.io/) and [Ruby](https://www.ruby-lang.org/).
4
+ Neutron follow the pattern `MVC` where the `V` is handled by Electron and (if you want) [ReactJS](https://facebook.github.io/react/).
5
+ The `MC` can be handled with pure Ruby code.
6
+
7
+
8
+ ## Installation
9
+
10
+ Install it yourself as:
11
+
12
+ $ gem install neutron-ruby-electron
13
+
14
+
15
+ ## Usage
16
+
17
+ First of all generate a new Neutron project:
18
+
19
+ $ neutron project_name
20
+
21
+ It will generate a new folder with the follow tree
22
+ ```
23
+ .
24
+ ├── LICENSE.txt
25
+ ├── README.md
26
+ └── src
27
+ ├── Gemfile
28
+ ├── Gemfile.lock
29
+ ├── assets
30
+ │   ├── index.html
31
+ │   ├── javascripts
32
+ │   │   ├── components
33
+ │   │   │   └── neutron_entry_point_component.js
34
+ │   │   └── neutron.js
35
+ │   └── stylesheets
36
+ ├── backend.rb
37
+ ├── main.rb
38
+ ├── main_window.js
39
+ ├── node_modules/
40
+ └── package.json
41
+ ```
42
+ You can run the just generated app with the command:
43
+
44
+ $ cd project_name/src
45
+ $ ruby main.rb
46
+
47
+ - The `main.rb` is the main of your app.
48
+ - The `main_window.js` is the Electron entry point file where the [Electron app](https://electron.atom.io/docs/api/app/) and the [main browser window](https://electron.atom.io/docs/api/browser-window/) are created.
49
+ - The `backend.rb` is a Ruby class where you can add your instance methods that can be called from the view, from your React components of your Javascript code. This class extends `Neutron::Controller` and looks like this:
50
+ ```
51
+ class MyController < Neutron::Controller
52
+ def ping
53
+ return 'pong'
54
+ end
55
+
56
+ def add(a, b)
57
+ return(a + b)
58
+ end
59
+ end
60
+ ```
61
+ - The `neutron_entry_point_component.js` is the main component that render all your application.
62
+
63
+
64
+ ### Communication between Electron and Ruby
65
+
66
+ Inside a React component you can import the Neutron module with
67
+
68
+ import neutron from 'neutron'
69
+
70
+ and use neutron to call a controller method like this:
71
+
72
+ neutron.send('method_name', [params], options).then((result) => {...}).catch((error) => {...})
73
+
74
+ You can call a Ruby Controller method with `neutron.send` method that return a JS promise with the return value of the method or an error.
75
+
76
+ For example you can have a component like this:
77
+ ```
78
+ import React from 'react'
79
+ import neutron from 'neutron'
80
+
81
+ export default class Sum extends React.Component {
82
+
83
+ constructor(props) {
84
+ super(props)
85
+ this.state = {}
86
+ }
87
+
88
+ sum() {
89
+ const a = parseInt(this.a.value)
90
+ const b = parseInt(this.b.value)
91
+ neutron.send('add', [a, b]).then((result) => {
92
+ this.setState({result: result})
93
+ }).catch((error) => {
94
+ console.log(error)
95
+ })
96
+ }
97
+
98
+ render() {
99
+ return(
100
+ <div>
101
+ <p>
102
+ <input ref={(input) => this.a = input}/>
103
+ +
104
+ <input ref={(input) => this.b = input}/>
105
+ = {this.state.result}
106
+ </p>
107
+ <p>
108
+ <button onClick={this.sum.bind(this)}>Calculate</button>
109
+ </p>
110
+ </div>
111
+ )
112
+ }
113
+ }
114
+ ```
115
+ In this componenet the sum is calculated by the method `add` in the Ruby controller.
116
+
117
+
118
+ ## Contributing
119
+
120
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pioz/neutron.
121
+
122
+
123
+ ## License
124
+
125
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "neutron"
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/neutron ADDED
@@ -0,0 +1,48 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'neutron/version'
5
+ require 'neutron/generator'
6
+ require 'optparse'
7
+
8
+ def parse_args(args)
9
+ options = {react: true, jquery: false, bootstrap: true, development: false, debug: false}
10
+ opt_parser = OptionParser.new do |opts|
11
+ opts.banner = "Usage: neutron [options] PROJECT_NAME"
12
+ opts.separator ''
13
+ opts.separator 'Options:'
14
+ opts.on('-r', '--[no-]react', 'Create a project with ReactJS support') do |opt|
15
+ options[:react] = opt
16
+ end
17
+ opts.on('-j', '--[no-]jquery', 'Create a project with jQuery support') do |opt|
18
+ options[:jquery] = opt
19
+ end
20
+ opts.on('-b', '--[no-]bootstrap', 'Create a project with Twitter Bootstrap support') do |opt|
21
+ options[:bootstrap] = opt
22
+ end
23
+ opts.on_tail('--dev-mode', 'Development mode') do |opt|
24
+ options[:development] = opt
25
+ end
26
+ opts.on_tail('-d', '--[no-]debug', 'Debug mode') do |opt|
27
+ options[:debug] = opt
28
+ end
29
+ opts.on_tail('-v', '--version', 'Print Neutron version') do
30
+ puts Neutron::VERSION
31
+ exit
32
+ end
33
+ opts.on_tail('-h', '--help', 'Help') do
34
+ puts opts
35
+ exit
36
+ end
37
+ end
38
+ opt_parser.parse!(args)
39
+ if args.size != 1
40
+ puts opt_parser.help
41
+ exit
42
+ end
43
+ options[:path] = args.first
44
+ options[:name] = File.basename(options[:path])
45
+ return options
46
+ end
47
+
48
+ Neutron::Generator.new(parse_args(ARGV)).generate!
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,27 @@
1
+ require 'sys/proctable'
2
+
3
+ module Neutron
4
+
5
+ class App
6
+
7
+ def initialize(controller:)
8
+ raise 'Controller must be inherit from Neutron::Controller' unless controller.is_a?(::Neutron::Controller)
9
+ @controller = controller
10
+ end
11
+
12
+ def run
13
+ path = File.expand_path('..', caller[0].split(':').first)
14
+ begin
15
+ Thread.new { @controller.run }
16
+ `cd '#{path}' && npm run boot`
17
+ ensure
18
+ process_path = File.join(path, 'node_modules/electron-prebuilt-compile/lib/es6-init.js')
19
+ p = Sys::ProcTable.ps.select { |p| p.cmdline.include?(process_path) }.first
20
+ Process.kill('HUP', p.pid) if p
21
+ @controller.stop
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,145 @@
1
+ require 'socket'
2
+ require 'json'
3
+
4
+ module Neutron
5
+
6
+ Thread.abort_on_exception = true
7
+
8
+ class Controller < UNIXServer
9
+
10
+ def initialize(path = '/tmp/neutron.sock')
11
+ File.delete(path) if File.exists?(path)
12
+
13
+ super(path)
14
+ @path = path
15
+ @run = true
16
+
17
+ trap('INT') do
18
+ STDOUT.puts 'Closing socket...'
19
+ self.stop
20
+ end
21
+ end
22
+
23
+ def stop
24
+ @run = false
25
+ self.close
26
+ File.delete(@path) if File.exists?(@path)
27
+ end
28
+
29
+ def run
30
+ while @run do
31
+ begin
32
+ Thread.fork(self.accept) do |sock| # begin
33
+ Thread.current[:threads] = {}
34
+ while (data = sock.gets)
35
+ break if data.empty?
36
+ data.chop!
37
+ # STDOUT.puts data
38
+ Thread.fork(sock, data, Thread.current[:threads]) do |sock, data, threads|
39
+ Thread.current[:sock] = sock
40
+ Thread.current[:threads] = threads
41
+ response = dispatch(data)
42
+ if response
43
+ send_string(sock, response.to_json + "\n")
44
+ end
45
+ end
46
+ end
47
+ end
48
+ rescue Errno::EBADF
49
+ # socket has been closed and server is waiting on socket.accept
50
+ end
51
+ end
52
+ end
53
+
54
+ def notify(event, result = nil)
55
+ if result.is_a?(Hash) && result[:error].is_a?(Hash)
56
+ result[:error][:code] ||= SERVER_ERROR
57
+ result[:error][:message] ||= 'Server error'
58
+ response = {jsonrpc: '2.0', error: result[:error], event: event, id: -1}
59
+ else
60
+ response = {jsonrpc: '2.0', result: result, event: event, id: -1}
61
+ end
62
+ sock = Thread.current[:sock]
63
+ if sock
64
+ send_string(sock, response.to_json + "\n")
65
+ end
66
+ end
67
+
68
+ PARSE_ERROR = -32700
69
+ INVALID_REQUEST = -32600
70
+ METHOD_NOT_FOUND = -32601
71
+ INTERNAL_ERROR = -32603
72
+ SERVER_ERROR = -32000
73
+
74
+ protected
75
+
76
+ def run_thread(&block)
77
+ #c = caller[0][/`.*'/][1..-2].to_sym
78
+ callers = caller.map{|c| c[/`.*'/][1..-2].to_sym}
79
+ c = callers.select{|c| Thread.current[:threads].keys.include?(c)}.first
80
+ sock = Thread.current[:sock]
81
+ Thread.current[:threads][c] << Thread.fork do
82
+ Thread.current[:sock] = sock
83
+ block.call
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def send_string(sock, s)
90
+ while(s.length > 0)
91
+ sent = sock.send(s, 0)
92
+ s = s[sent..-1]
93
+ end
94
+ sock.flush
95
+ end
96
+
97
+ def dispatch(data)
98
+ begin
99
+ jsonrpc = JSON.parse(data)
100
+ rescue JSON::ParserError
101
+ return {jsonrpc: '2.0', error: {code: PARSE_ERROR, message: 'Parse error'}, id: nil}
102
+ end
103
+ if jsonrpc['jsonrpc'] != '2.0' || !jsonrpc['id'].is_a?(Integer) || jsonrpc['id'] < 0
104
+ return {jsonrpc: '2.0', error: {code: INVALID_REQUEST, message: 'Invalid request'}, id: jsonrpc['id']}
105
+ end
106
+ if !self.respond_to?(jsonrpc['method'])
107
+ return {jsonrpc: '2.0', error: {code: METHOD_NOT_FOUND, message: 'Method not found'}, id: jsonrpc['id']}
108
+ end
109
+
110
+ # begin
111
+ # result = run_method(jsonrpc['method'], jsonrpc['params'], once: jsonrpc['once'])
112
+ # rescue Exception => e
113
+ # return {jsonrpc: '2.0', error: {code: INTERNAL_ERROR, message: e.message}, id: jsonrpc['id']}
114
+ # end
115
+ result = run_method(jsonrpc['method'], jsonrpc['params'], once: jsonrpc['once'])
116
+
117
+ if result.is_a?(Hash) && result[:error].is_a?(Hash)
118
+ result[:error][:code] ||= SERVER_ERROR
119
+ result[:error][:message] ||= 'Server error'
120
+ return {jsonrpc: '2.0', error: result[:error], event: jsonrpc['method'], id: jsonrpc['id']}
121
+ else
122
+ r = result.is_a?(Hash) && !result[:result].nil? ? result[:result] : result
123
+ # Reply with a non error result
124
+ return {jsonrpc: '2.0', result: r, event: jsonrpc['method'], id: jsonrpc['id']}
125
+ end
126
+ end
127
+
128
+ def run_method(method, params, once: true)
129
+ if once
130
+ Thread.current[:threads][method.to_sym] ||= []
131
+ Thread.current[:threads][method.to_sym].each do |t|
132
+ t.kill if t.alive?
133
+ Thread.current[:threads][method.to_sym].delete(t)
134
+ end
135
+ Thread.current[:threads][method.to_sym] << Thread.current
136
+ end
137
+ if params
138
+ return self.__send__(method, *params)
139
+ else
140
+ return self.__send__(method)
141
+ end
142
+ end
143
+
144
+ end
145
+ end
@@ -0,0 +1,77 @@
1
+ require 'thor'
2
+ require 'json'
3
+ require 'etc'
4
+
5
+
6
+ module Neutron
7
+
8
+ TEMPLATE_PATH = File.expand_path('../template', __FILE__)
9
+ NEUTRON_NPM_PACKAGE_PATH = File.expand_path('../neutron_npm_package', __FILE__)
10
+
11
+ class Generator < Thor::Group
12
+ include Thor::Actions
13
+
14
+ def initialize(options)
15
+ super()
16
+ @options = options
17
+ end
18
+
19
+ def self.source_root
20
+ File.dirname(__FILE__)
21
+ end
22
+
23
+ def generate!
24
+ self.copy_template
25
+ self.generate_package_json
26
+ end
27
+
28
+ def copy_template
29
+ directory(TEMPLATE_PATH, options[:path], exclude_pattern: /components/)
30
+ if options[:react]
31
+ directory(File.join(TEMPLATE_PATH, 'src/assets/javascripts/components'), File.join(options[:path], 'src/assets/javascripts/components'))
32
+ end
33
+ inside(File.join(options[:path], 'src')) do
34
+ system 'bundle install'
35
+ end
36
+ end
37
+
38
+ def generate_package_json
39
+ say_status :generate, File.join(options[:path], 'src', 'package.json')
40
+ inside(File.join(options[:path], 'src')) do
41
+ # Init NPM
42
+ `npm init -y`
43
+ # Install local neutron npm package
44
+ system "npm install --save-dev #{NEUTRON_NPM_PACKAGE_PATH}"
45
+ # Install NPM packages
46
+ %w(electron electron-prebuilt-compile).each do |package|
47
+ system "npm install --save-dev #{package}"
48
+ end
49
+ if options[:jquery] || options[:bootstrap]
50
+ system "npm install --save jquery"
51
+ end
52
+ if options[:bootstrap]
53
+ system "npm install --save bootstrap"
54
+ end
55
+ if options[:react]
56
+ system "npm install --save react"
57
+ system "npm install --save react-dom"
58
+ end
59
+ # Edit package.json
60
+ username = Etc.getlogin.downcase
61
+ json = JSON.parse(File.read('package.json'))
62
+ json['name'] = options[:name]
63
+ json['description'] = 'Another Neutron app'
64
+ json['repository'] = "https://github.com/#{username}/#{options[:name]}"
65
+ json['author'] = username
66
+ json['license'] = 'MIT'
67
+ json['main'] = 'main_window.js'
68
+ json['scripts']['boot'] = './node_modules/.bin/electron .'
69
+ json['scripts']['buildjs'] = 'NODE_ENV=production ./node_modules/.bin/electron-compile .'
70
+ #json['babel'] = {'presets' => ['es2015']}
71
+ File.open('package.json', 'w') {|f| f << json.to_json}
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,128 @@
1
+ // import Events from 'events'
2
+ // import net from 'net'
3
+ const Events = require('events')
4
+ const net = require('net')
5
+
6
+
7
+ let global_jsonrpc_id = 1
8
+
9
+
10
+ class Neutron extends Events.EventEmitter {
11
+
12
+ constructor(path = '/tmp/neutron.sock') {
13
+ super()
14
+ this.setMaxListeners(0)
15
+ this.promises = {}
16
+ this.path = path
17
+ this.socket = new net.Socket()
18
+ this.msg = ''
19
+
20
+ this.socket.on('data', (data) => {
21
+ const s = data.toString()
22
+ this.msg += s
23
+ if (s[s.length-1] === "\n") {
24
+ const msg = this.msg.slice(0, -1)
25
+ this.msg = ''
26
+ const messages = msg.split("\n")
27
+ for (let i = 0; i < messages.length; i++) {
28
+ try {
29
+ const jsonrpc = JSON.parse(messages[i])
30
+ if (typeof(jsonrpc['id']) !== 'number') {
31
+ this.emit('err', {message: 'Missing id on jsonrpc reply'})
32
+ this.emit('all')
33
+ } else {
34
+ if (jsonrpc['error']) {
35
+ const error = jsonrpc['error']
36
+ error.toString = () => {
37
+ this.message
38
+ }
39
+ if (jsonrpc['event'])
40
+ this.emit(`err-${jsonrpc['event']}`, error)
41
+ this.emit(`err-${jsonrpc['id']}`, error)
42
+ this.emit('all')
43
+ } else {
44
+ if (jsonrpc['event'])
45
+ this.emit(jsonrpc['event'], jsonrpc['result'])
46
+ this.emit(`json-${jsonrpc['id']}`, jsonrpc['result'])
47
+ this.emit('all')
48
+ }
49
+ }
50
+ } catch(error) {
51
+ this.emit('err', {message: 'Error to parse the response'})
52
+ this.emit('all')
53
+ }
54
+ }
55
+ }
56
+ })
57
+
58
+ this.socket.on('connect', () => {
59
+ this.emit('connect')
60
+ this.emit('all')
61
+ })
62
+
63
+ this.socket.on('end', () => {
64
+ this.emit('end')
65
+ this.emit('all')
66
+ this.reconnect(2000)
67
+ })
68
+
69
+ this.socket.on('error', (error) => {
70
+ this.emit('err-connection', error)
71
+ this.emit('err', error)
72
+ this.emit('all')
73
+ this.reconnect(2000)
74
+ })
75
+ }
76
+
77
+ connect() {
78
+ // remote.process.on('uncaughtException', (error) => {
79
+ // this.emit('connection-error', error)
80
+ // this.emit('all')
81
+ // this.emit('all-errors')
82
+ // this.reconnect(2000)
83
+ // })
84
+ this.socket.connect({path: this.path})
85
+ }
86
+
87
+ reconnect(timeout = 0) {
88
+ setTimeout(() => {
89
+ console.log('Try to reconnect to the socket ' + this.path + '...')
90
+ this.socket.end()
91
+ this.socket.connect({path: this.path})
92
+ }, timeout)
93
+ }
94
+
95
+ close() {
96
+ this.socket.end()
97
+ }
98
+
99
+ real_send(method, params, {once = true} = {}) {
100
+ const id = global_jsonrpc_id++
101
+ this.socket.write(JSON.stringify({
102
+ jsonrpc: '2.0',
103
+ method: method,
104
+ params: params,
105
+ once: once,
106
+ id: id
107
+ }) + "\n")
108
+ return id
109
+ }
110
+
111
+ send(method, params, {once = true} = {}) {
112
+ const id = this.real_send(method, params, {once: once})
113
+ return new Promise((resolve, reject) => {
114
+ this.on(`json-${id}`, resolve)
115
+ this.on(`err-${id}`, reject)
116
+ this.on('err', reject)
117
+ })
118
+ }
119
+
120
+ }
121
+
122
+ const neutron = new Neutron()
123
+ neutron.on('connect', () => {
124
+ console.log('Electron connected to Ruby!')
125
+ })
126
+ neutron.connect()
127
+ // export default neutron
128
+ module.exports = neutron
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "neutron",
3
+ "version": "0.1.0",
4
+ "description": "Mini framework to build desktop app with Electron and Ruby",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/pioz/neutron.git"
12
+ },
13
+ "keywords": [
14
+ "ruby",
15
+ "electron"
16
+ ],
17
+ "author": "pioz",
18
+ "license": "MIT",
19
+ "bugs": {
20
+ "url": "https://github.com/pioz/neutron/issues"
21
+ },
22
+ "homepage": "https://github.com/pioz/neutron#readme"
23
+ }
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) <%= Time.now.year %> <%= Etc.getlogin.downcase %>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ # <%= options[:name] %>
2
+
3
+ Project README here...
@@ -0,0 +1,94 @@
1
+ require 'bundler'
2
+
3
+ PACKAGE_NAME = '<%= options[:name] %>'
4
+ VERSION = '0.1.0'
5
+ RUBY_V = '2.2'
6
+ TRAVELING_RUBY_VERSION = "20150715-#{RUBY_V}.2"
7
+
8
+ desc 'Package your app'
9
+ task package: ['package:linux:x86', 'package:linux:x86_64', 'package:osx']
10
+
11
+ namespace :package do
12
+ namespace :linux do
13
+ desc 'Package your app for Linux x86'
14
+ task :x86 => "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86.tar.gz" do
15
+ create_package('linux-x86')
16
+ end
17
+
18
+ desc 'Package your app for Linux x86_64'
19
+ task :x86_64 => "packaging/traveling-ruby-#{TRAVELING_RUBY_VERSION}-linux-x86_64.tar.gz" do
20
+ create_package('linux-x86_64')
21
+ end
22
+ end
23
+
24
+ desc 'Package your app for OS X'
25
+ task :osx do
26
+ create_package('osx')
27
+ end
28
+
29
+ end
30
+
31
+ def create_package(target)
32
+ if RUBY_VERSION !~ /^#{RUBY_V}\./
33
+ abort "You can only use Ruby #{RUBY_V}, because that's what Traveling Ruby uses."
34
+ end
35
+ package_dir = "#{PACKAGE_NAME}-#{VERSION}-#{target}"
36
+ sh "rm -rf #{package_dir}"
37
+ sh "rm -rf tmp/"
38
+ sh "mkdir tmp"
39
+ sh "mkdir -p #{package_dir}/lib/ruby"
40
+ sh "rsync -a src #{package_dir}/lib"
41
+ download_runtime('osx')
42
+ sh "tar -xzf tmp/traveling-ruby-#{TRAVELING_RUBY_VERSION}-#{target}.tar.gz -C #{package_dir}/lib/ruby"
43
+ create_wrapper("#{package_dir}/#{PACKAGE_NAME}")
44
+ sh "chmod 755 #{package_dir}/#{PACKAGE_NAME}"
45
+ bundle_install
46
+ sh "cp -pR tmp/vendor #{package_dir}/lib/"
47
+ sh "cp src/Gemfile src/Gemfile.lock #{package_dir}/lib/vendor/"
48
+ sh "mkdir #{package_dir}/lib/vendor/.bundle"
49
+ sh "cp tmp/bundler-config #{package_dir}/lib/vendor/.bundle/config"
50
+ if !ENV['DIR_ONLY']
51
+ sh "tar -czf #{package_dir}.tar.gz #{package_dir}"
52
+ sh "rm -rf #{package_dir}"
53
+ end
54
+ sh "rm -rf tmp/"
55
+ end
56
+
57
+ def download_runtime(target)
58
+ sh "cd tmp && curl -L -O --fail https://d6r77u77i8pq3.cloudfront.net/releases/traveling-ruby-#{TRAVELING_RUBY_VERSION}-#{target}.tar.gz"
59
+ end
60
+
61
+ def bundle_install
62
+ sh "cp src/Gemfile src/Gemfile.lock tmp/"
63
+ Bundler.with_clean_env do
64
+ sh "cd tmp && env BUNDLE_IGNORE_CONFIG=1 bundle install --path vendor --without development"
65
+ <% if options[:development] -%>
66
+ sh "cd tmp && env BUNDLE_IGNORE_CONFIG=1 bundle package --all"
67
+ sh "mv tmp/vendor/cache/neutron tmp/vendor/ruby/#{RUBY_V}.0/gems"
68
+ <% end -%>
69
+ end
70
+ sh "rm -rf tmp/vendor/cache"
71
+ sh "rm -rf tmp/vendor/ruby/#{RUBY_V}.0/cache"
72
+ bundle_config = <<-CONFIG
73
+ BUNDLE_PATH: .
74
+ BUNDLE_WITHOUT: development
75
+ BUNDLE_DISABLE_SHARED_GEMS: '1'
76
+ CONFIG
77
+ File.open('tmp/bundler-config', 'w') {|f| f << bundle_config.gsub(/^\s{4}/, '') }
78
+ end
79
+
80
+ def create_wrapper(out)
81
+ wrapper = <<-WRAPPER
82
+ #!/bin/bash
83
+ set -e
84
+
85
+ SELFDIR="`dirname \"$0\"`"
86
+ SELFDIR="`cd \"$SELFDIR\" && pwd`"
87
+
88
+ export BUNDLE_GEMFILE="$SELFDIR/lib/vendor/Gemfile"
89
+ unset BUNDLE_IGNORE_CONFIG
90
+
91
+ exec "NODE_ENV=production $SELFDIR/lib/ruby/bin/ruby" -rbundler/setup "$SELFDIR/lib/src/main.rb"
92
+ WRAPPER
93
+ File.open(out, 'w') {|f| f << wrapper.gsub(/^\s{4}/, '') }
94
+ end
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ <% if options[:development] -%>
4
+ gem 'neutron', path: '<%= File.expand_path('..', __FILE__) %>'
5
+ <% else -%>
6
+ gem 'neutron'
7
+ <% end -%>
@@ -0,0 +1,27 @@
1
+ <html>
2
+ <head>
3
+ <script src='./javascripts/neutron.js'></script>
4
+ <link href='./stylesheets/neutron.less' type='text/less' rel='stylesheet'>
5
+ </head>
6
+ <body>
7
+ <% if options[:react] -%>
8
+ <div id='react-entry-point'></div>
9
+ <script>
10
+ ReactDOM.render(React.createElement(NeutronEntryPointComponent, null), document.getElementById('react-entry-point'))
11
+ </script>
12
+ <% else -%>
13
+ <div>
14
+ <h2>Welcome to NEUTRON</h2>
15
+ <p>
16
+ <button id='b'>Try Neutron</button>
17
+ <script>
18
+ document.getElementById('b').addEventListener('click', () => {
19
+ for(let i = 0; i < 10; i++)
20
+ neutron.send('add', [0, i], {once: false}).then(result => console.log(result)).catch(e => console.log(e))
21
+ })
22
+ </script>
23
+ </p>
24
+ </div>
25
+ <% end -%>
26
+ </body>
27
+ </html>
@@ -0,0 +1,22 @@
1
+ import React from 'react'
2
+ import neutron from 'neutron'
3
+
4
+ export default class NeutronEntryPointComponent extends React.Component {
5
+
6
+ button_click() {
7
+ for(let i = 0; i < 10; i++)
8
+ neutron.send('add', [0, i], {once: false}).then(result => console.log(result)).catch(e => console.log(e))
9
+ }
10
+
11
+ render() {
12
+ return(
13
+ <div>
14
+ <h2>Welcome to NEUTRON</h2>
15
+ <p>
16
+ <button className='btn btn-default' onClick={this.button_click}>Try Neutron</button>
17
+ </p>
18
+ </div>
19
+ )
20
+ }
21
+
22
+ }
@@ -0,0 +1,36 @@
1
+ import React from 'react'
2
+ import neutron from 'neutron'
3
+
4
+ export default class Sum extends React.Component {
5
+
6
+ constructor(props) {
7
+ super(props)
8
+ this.state = {}
9
+ }
10
+
11
+ sum() {
12
+ const a = parseInt(this.a.value)
13
+ const b = parseInt(this.b.value)
14
+ neutron.send('add', [a, b]).then((result) => {
15
+ this.setState({result: result})
16
+ }).catch((error) => {
17
+ console.log(error)
18
+ })
19
+ }
20
+
21
+ render() {
22
+ return(
23
+ <div>
24
+ <p>
25
+ <input ref={(input) => this.a = input}/>
26
+ +
27
+ <input ref={(input) => this.b = input}/>
28
+ = {this.state.result}
29
+ </p>
30
+ <p>
31
+ <button onClick={this.sum.bind(this)}>Calculate</button>
32
+ </p>
33
+ </div>
34
+ )
35
+ }
36
+ }
@@ -0,0 +1,23 @@
1
+ <% if options[:jquery] || options[:bootstrap] -%>
2
+ import jQuery from 'jquery'
3
+ <% end -%>
4
+ <% if options[:react] -%>
5
+ import ReactDOM from 'react-dom'
6
+ import React from 'react'
7
+ import NeutronEntryPointComponent from './javascripts/components/neutron_entry_point_component'
8
+ <% else -%>
9
+ import neutron from 'neutron'
10
+ <% end -%>
11
+ <% if options[:jquery] || options[:bootstrap] -%>
12
+ window.jQuery = window.$ = jQuery
13
+ <% end -%>
14
+ <% if options[:react] -%>
15
+ window.ReactDOM = ReactDOM
16
+ window.React = React
17
+ window.NeutronEntryPointComponent = NeutronEntryPointComponent
18
+ <% else -%>
19
+ window.neutron = neutron
20
+ <% end -%>
21
+ <% if options[:bootstrap] -%>
22
+ require('bootstrap')
23
+ <% end -%>
@@ -0,0 +1,4 @@
1
+ <% if options[:bootstrap] -%>
2
+ @import '../../node_modules/bootstrap/less/bootstrap.less';
3
+ @icon-font-path: '../../node_modules/bootstrap/fonts/';
4
+ <% end -%>
@@ -0,0 +1,9 @@
1
+ require 'neutron/controller'
2
+
3
+ class Backend < Neutron::Controller
4
+
5
+ def add(a, b)
6
+ return a + b
7
+ end
8
+
9
+ end
@@ -0,0 +1,11 @@
1
+ <% if options[:development] -%>
2
+ require 'bundler'
3
+ ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile', __FILE__)
4
+ Bundler.require
5
+
6
+ <% end -%>
7
+ require 'neutron/app'
8
+ require File.expand_path('../backend.rb', __FILE__)
9
+
10
+ app = Neutron::App.new(controller: Backend.new)
11
+ app.run
@@ -0,0 +1,40 @@
1
+ import { app, BrowserWindow } from 'electron'
2
+ import { enableLiveReload } from 'electron-compile'
3
+ import path from 'path'
4
+ import url from 'url'
5
+
6
+ if (process.env.NODE_ENV !== 'production')
7
+ enableLiveReload()
8
+
9
+ let win = null
10
+
11
+ function createWindow() {
12
+ win = new BrowserWindow({title: 'Another Neutron app', width: 800, height: 600})
13
+ win.loadURL(url.format({
14
+ pathname: path.join(__dirname, 'assets/index.html'),
15
+ protocol: 'file:',
16
+ slashes: true
17
+ }))
18
+ if (process.env.NODE_ENV !== 'production')
19
+ win.webContents.openDevTools()
20
+ win.on('close', (event) => {
21
+ if (app.quitting)
22
+ win = null
23
+ else {
24
+ event.preventDefault()
25
+ win.hide()
26
+ }
27
+ })
28
+ }
29
+
30
+ app.on('ready', createWindow)
31
+
32
+ app.on('window-all-closed', () => {
33
+ if (process.platform !== 'darwin') {
34
+ app.quit()
35
+ }
36
+ })
37
+
38
+ app.on('activate', () => { win.show() })
39
+
40
+ app.on('before-quit', () => app.quitting = true)
@@ -0,0 +1,3 @@
1
+ module Neutron
2
+ VERSION = "0.1.0"
3
+ end
data/neutron.gemspec ADDED
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'neutron/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "neutron-ruby-electron"
8
+ spec.version = Neutron::VERSION
9
+ spec.authors = ["pioz"]
10
+ spec.email = ["epilotto@gmx.com"]
11
+
12
+ spec.summary = %q{Mini framework to build desktop app with Electron and Ruby}
13
+ spec.description = %q{Mini framework to build desktop app with Electron and Ruby}
14
+ spec.homepage = "https://github.com/pioz/neutron"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org/"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against " \
23
+ "public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "bin"
30
+ spec.executables = ["neutron"]
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.14"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+
36
+ spec.add_runtime_dependency "thor", "~> 0.19"
37
+ spec.add_runtime_dependency "sys-proctable", "~> 1.1"
38
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: neutron-ruby-electron
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - pioz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.19'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.19'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sys-proctable
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
69
+ description: Mini framework to build desktop app with Electron and Ruby
70
+ email:
71
+ - epilotto@gmx.com
72
+ executables:
73
+ - neutron
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - bin/console
83
+ - bin/neutron
84
+ - bin/setup
85
+ - lib/neutron/app.rb
86
+ - lib/neutron/controller.rb
87
+ - lib/neutron/generator.rb
88
+ - lib/neutron/neutron_npm_package/index.js
89
+ - lib/neutron/neutron_npm_package/package.json
90
+ - lib/neutron/template/LICENSE.txt.tt
91
+ - lib/neutron/template/README.md.tt
92
+ - lib/neutron/template/Rakefile.tt
93
+ - lib/neutron/template/src/Gemfile.tt
94
+ - lib/neutron/template/src/assets/index.html.tt
95
+ - lib/neutron/template/src/assets/javascripts/components/neutron_entry_point_component.js.tt
96
+ - lib/neutron/template/src/assets/javascripts/components/sum.js
97
+ - lib/neutron/template/src/assets/javascripts/neutron.js.tt
98
+ - lib/neutron/template/src/assets/stylesheets/neutron.less.tt
99
+ - lib/neutron/template/src/backend.rb.tt
100
+ - lib/neutron/template/src/main.rb.tt
101
+ - lib/neutron/template/src/main_window.js.tt
102
+ - lib/neutron/version.rb
103
+ - neutron.gemspec
104
+ homepage: https://github.com/pioz/neutron
105
+ licenses:
106
+ - MIT
107
+ metadata:
108
+ allowed_push_host: https://rubygems.org/
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.6.12
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Mini framework to build desktop app with Electron and Ruby
129
+ test_files: []