apiotics_factory 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/License +9 -0
- data/bin/apiotics_factory +3 -0
- data/lib/apiotics_factory/clear_credentials.rb +25 -0
- data/lib/apiotics_factory/configuration.rb +30 -0
- data/lib/apiotics_factory/generator.rb +133 -0
- data/lib/apiotics_factory/my_command.rb +15 -0
- data/lib/apiotics_factory/portal.rb +33 -0
- data/lib/apiotics_factory/publish.rb +83 -0
- data/lib/apiotics_factory/underscore.rb +11 -0
- data/lib/apiotics_factory/version.rb +3 -0
- data/lib/apiotics_factory.rb +28 -0
- data/lib/templates/apiotics_driver.rb.erb +244 -0
- data/lib/templates/apiotics_interfaces.rb.erb +33 -0
- data/lib/templates/config.json.erb +172 -0
- data/lib/templates/exec.rb.erb +8 -0
- data/lib/templates/grove_pi/grove_pi.rb +270 -0
- data/lib/templates/grove_pi/i2c/device/acm1602ni.rb +19 -0
- data/lib/templates/grove_pi/i2c/device/adt7410.rb +149 -0
- data/lib/templates/grove_pi/i2c/device/aqm0802.rb +70 -0
- data/lib/templates/grove_pi/i2c/device/bmp180.rb +207 -0
- data/lib/templates/grove_pi/i2c/device/d6t-44l.rb +52 -0
- data/lib/templates/grove_pi/i2c/device/hd44780.rb +172 -0
- data/lib/templates/grove_pi/i2c/device/hdc1000.rb +42 -0
- data/lib/templates/grove_pi/i2c/device/mpl115a2.rb +38 -0
- data/lib/templates/grove_pi/i2c/driver/gpio.rb +213 -0
- data/lib/templates/grove_pi/i2c/driver/i2c-dev.rb +60 -0
- data/lib/templates/grove_pi/i2c/driver.rb +28 -0
- data/lib/templates/grove_pi/i2c/i2c.rb +45 -0
- metadata +114 -0
@@ -0,0 +1,213 @@
|
|
1
|
+
require_relative "../../i2c"
|
2
|
+
require_relative "../../i2c/driver"
|
3
|
+
=begin
|
4
|
+
Generic software I2C Driver based on /sys/class/gpio.
|
5
|
+
THIS MODULE WORKS WITH VERY SLOW SPEED ABOUT JUST 1kHz (normaly 100kHz).
|
6
|
+
=end
|
7
|
+
|
8
|
+
class I2CDevice::Driver::GPIO < I2CDevice::Driver::Base
|
9
|
+
@@DEBUG = false
|
10
|
+
|
11
|
+
def self.export(pin) #:nodoc:
|
12
|
+
File.open("/sys/class/gpio/export", "w") do |f|
|
13
|
+
f.syswrite(pin)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.unexport(pin) #:nodoc:
|
18
|
+
File.open("/sys/class/gpio/unexport", "w") do |f|
|
19
|
+
f.syswrite(pin)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.direction(pin, direction) #:nodoc:
|
24
|
+
# [:in, :out, :high, :low].include?(direction) or raise "direction must be :in, :out, :high or :low"
|
25
|
+
File.open("/sys/class/gpio/gpio#{pin}/direction", "w") do |f|
|
26
|
+
f.syswrite(direction)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.read(pin) #:nodoc:
|
31
|
+
File.open("/sys/class/gpio/gpio#{pin}/value", "r") do |f|
|
32
|
+
f.sysread(1).to_i
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.write(pin, val) #:nodoc:
|
37
|
+
File.open("/sys/class/gpio/gpio#{pin}/value", "w") do |f|
|
38
|
+
f.syswrite(val ? "1" : "0")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.finalizer(ports) #:nodoc:
|
43
|
+
proc do
|
44
|
+
ports.each do |pin|
|
45
|
+
GPIO.unexport(pin)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Pin-number of SDA
|
51
|
+
attr_reader :sda
|
52
|
+
# Pin-number of SCL
|
53
|
+
attr_reader :scl
|
54
|
+
# Clock speed in kHz
|
55
|
+
attr_reader :speed
|
56
|
+
|
57
|
+
# <tt>opts[:sda]</tt> :: [Integer] Pin-number of SDA
|
58
|
+
# <tt>opts[:scl]</tt> :: [Integer] Pin-number of SCL
|
59
|
+
# <tt>[ opts[:speed] = 1 ]</tt> :: [Integer] Clock speed in kHz
|
60
|
+
def initialize(opts={})
|
61
|
+
@sda = opts[:sda] or raise "opts[:sda] = [gpio pin number] is required"
|
62
|
+
@scl = opts[:scl] or raise "opts[:scl] = [gpio pin number] is required"
|
63
|
+
@speed = opts[:speed] || 1 # kHz but insane
|
64
|
+
@clock = 1.0 / (@speed * 1000)
|
65
|
+
|
66
|
+
begin
|
67
|
+
GPIO.export(@sda)
|
68
|
+
GPIO.export(@scl)
|
69
|
+
rescue Errno::EBUSY => e
|
70
|
+
end
|
71
|
+
ObjectSpace.define_finalizer(self, self.class.finalizer([@scl, @sda]))
|
72
|
+
begin
|
73
|
+
GPIO.direction(@sda, :high)
|
74
|
+
GPIO.direction(@scl, :high)
|
75
|
+
GPIO.direction(@sda, :in)
|
76
|
+
GPIO.direction(@scl, :in)
|
77
|
+
rescue Errno::EACCES => e # writing to gpio after export is failed in a while
|
78
|
+
retry
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Interface of I2CDevice::Driver
|
83
|
+
def i2cget(address, param, length=1)
|
84
|
+
ret = ""
|
85
|
+
start_condition
|
86
|
+
unless write( (address << 1) + 0)
|
87
|
+
raise I2CDevice::I2CIOError, "Unknown slave device (address:#{address})"
|
88
|
+
end
|
89
|
+
write(param)
|
90
|
+
stop_condition # AVR stucked with SCL low without this (Does not AVR support Sr condition?)
|
91
|
+
start_condition
|
92
|
+
unless write( (address << 1) + 1)
|
93
|
+
raise I2CDevice::I2CIOError, "Unknown slave device (address:#{address})"
|
94
|
+
end
|
95
|
+
length.times do |n|
|
96
|
+
ret << read(n != length - 1).chr
|
97
|
+
end
|
98
|
+
ret
|
99
|
+
ensure
|
100
|
+
stop_condition
|
101
|
+
end
|
102
|
+
|
103
|
+
# Interface of I2CDevice::Driver
|
104
|
+
def i2cset(address, *data)
|
105
|
+
sent = 0
|
106
|
+
start_condition
|
107
|
+
unless write( (address << 1) + 0)
|
108
|
+
raise I2CDevice::I2CIOError, "Unknown slave device (address:#{address})"
|
109
|
+
end
|
110
|
+
data.each do |c|
|
111
|
+
unless write(c)
|
112
|
+
break
|
113
|
+
end
|
114
|
+
sent += 1
|
115
|
+
end
|
116
|
+
sent
|
117
|
+
ensure
|
118
|
+
stop_condition
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
# Send start condition (or repeated start condition)
|
124
|
+
# raise I2CDevice::I2CBUSBusy if SCL line is low
|
125
|
+
def start_condition
|
126
|
+
p :start_condition if @@DEBUG
|
127
|
+
sleep @clock
|
128
|
+
GPIO.direction(@sda, :in)
|
129
|
+
GPIO.direction(@scl, :in)
|
130
|
+
if GPIO.read(@scl) == 0
|
131
|
+
raise I2CDevice::I2CBUSBusy, "BUS is busy"
|
132
|
+
end
|
133
|
+
|
134
|
+
sleep @clock / 2
|
135
|
+
GPIO.direction(@scl, :high)
|
136
|
+
sleep @clock / 2
|
137
|
+
GPIO.direction(@sda, :low)
|
138
|
+
sleep @clock
|
139
|
+
end
|
140
|
+
|
141
|
+
# Send stop condition.
|
142
|
+
def stop_condition
|
143
|
+
p :stop_condition if @@DEBUG
|
144
|
+
GPIO.direction(@scl, :low)
|
145
|
+
sleep @clock / 2
|
146
|
+
GPIO.direction(@sda, :low)
|
147
|
+
sleep @clock / 2
|
148
|
+
GPIO.direction(@scl, :in)
|
149
|
+
sleep @clock / 2
|
150
|
+
GPIO.direction(@sda, :in)
|
151
|
+
sleep @clock / 2
|
152
|
+
end
|
153
|
+
|
154
|
+
# Write one _byte_ to BUS.
|
155
|
+
def write(byte)
|
156
|
+
p [:write, byte] if @@DEBUG
|
157
|
+
GPIO.direction(@scl, :low)
|
158
|
+
sleep @clock
|
159
|
+
|
160
|
+
7.downto(0) do |n|
|
161
|
+
GPIO.direction(@sda, byte[n] == 1 ? :high : :low)
|
162
|
+
GPIO.direction(@scl, :in)
|
163
|
+
until GPIO.read(@scl) == 1
|
164
|
+
# clock streching
|
165
|
+
sleep @clock
|
166
|
+
end
|
167
|
+
sleep @clock
|
168
|
+
GPIO.direction(@scl, :low)
|
169
|
+
GPIO.write(@sda, false)
|
170
|
+
sleep @clock
|
171
|
+
end
|
172
|
+
|
173
|
+
GPIO.direction(@sda, :in)
|
174
|
+
GPIO.direction(@scl, :in)
|
175
|
+
sleep @clock / 2
|
176
|
+
ack = GPIO.read(@sda) == 0
|
177
|
+
sleep @clock / 2
|
178
|
+
while GPIO.read(@scl) == 0
|
179
|
+
sleep @clock
|
180
|
+
end
|
181
|
+
GPIO.direction(@scl, :low)
|
182
|
+
ack
|
183
|
+
end
|
184
|
+
|
185
|
+
# Read one byte from BUS.
|
186
|
+
# <tt>ack</tt> :: [true|flase] Send ack for this read.
|
187
|
+
# Returns :: [Integer] Byte
|
188
|
+
def read(ack=true)
|
189
|
+
p [:read, ack] if @@DEBUG
|
190
|
+
ret = 0
|
191
|
+
|
192
|
+
GPIO.direction(@scl, :low)
|
193
|
+
sleep @clock
|
194
|
+
GPIO.direction(@sda, :in)
|
195
|
+
|
196
|
+
8.times do
|
197
|
+
GPIO.direction(@scl, :in)
|
198
|
+
sleep @clock / 2
|
199
|
+
ret = (ret << 1) | GPIO.read(@sda)
|
200
|
+
sleep @clock / 2
|
201
|
+
GPIO.direction(@scl, :low)
|
202
|
+
sleep @clock
|
203
|
+
end
|
204
|
+
|
205
|
+
GPIO.direction(@sda, ack ? :low : :high)
|
206
|
+
|
207
|
+
GPIO.write(@scl, true)
|
208
|
+
sleep @clock
|
209
|
+
GPIO.write(@scl, false)
|
210
|
+
sleep @clock
|
211
|
+
ret
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative "../../i2c"
|
2
|
+
require_relative "../../i2c/driver"
|
3
|
+
|
4
|
+
class I2CDevice::Driver::I2CDev < I2CDevice::Driver::Base
|
5
|
+
# ioctl command
|
6
|
+
# Ref. https://www.kernel.org/pub/linux/kernel/people/marcelo/linux-2.4/include/linux/i2c.h
|
7
|
+
I2C_RETRIES = 0x0701
|
8
|
+
I2C_TIMEOUT = 0x0702
|
9
|
+
I2C_SLAVE = 0x0703
|
10
|
+
I2C_SLAVE_FORCE = 0x0706
|
11
|
+
I2C_TENBIT = 0x0704
|
12
|
+
I2C_FUNCS = 0x0705
|
13
|
+
I2C_RDWR = 0x0707
|
14
|
+
I2C_SMBUS = 0x0720
|
15
|
+
I2C_UDELAY = 0x0705
|
16
|
+
I2C_MDELAY = 0x0706
|
17
|
+
|
18
|
+
# This depends on /dev/i2c-* (i2c-dev) feature on Linux. You may load i2c-dev kernel module.
|
19
|
+
# <tt>path</tt> :: [String] Path to /dev/i2c-* file.
|
20
|
+
# <tt>force</tt> :: [Boolean] Force the driver to read or set values even if the device is in use.
|
21
|
+
# This is dangerous, as it can seriously confuse the kernel driver in question.
|
22
|
+
# It can also cause i2cget and i2cset to writ to the wrong register.
|
23
|
+
# Use at your own risk and only if you know what you're doing.
|
24
|
+
#
|
25
|
+
# If _path_ is not specified, this method use <tt>Dir.glob("/dev/i2c-*").last</tt> for _path_
|
26
|
+
def initialize(path=nil, force=false)
|
27
|
+
if path.nil?
|
28
|
+
path = Dir.glob("/dev/i2c-*").sort.last
|
29
|
+
end
|
30
|
+
|
31
|
+
unless File.exist?(path)
|
32
|
+
raise I2CDevice::I2CIOError, "/dev/i2c-0 is required"
|
33
|
+
end
|
34
|
+
|
35
|
+
@path = path
|
36
|
+
@slave_command = force ? I2C_SLAVE_FORCE : I2C_SLAVE
|
37
|
+
end
|
38
|
+
|
39
|
+
# Interface of I2CDevice::Driver
|
40
|
+
def i2cget(address, param, length)
|
41
|
+
i2c = File.open(@path, "r+")
|
42
|
+
i2c.ioctl(@slave_command, address)
|
43
|
+
i2c.syswrite(param.chr) unless param.nil?
|
44
|
+
ret = i2c.sysread(length)
|
45
|
+
i2c.close
|
46
|
+
ret
|
47
|
+
rescue Errno::EIO => e
|
48
|
+
raise I2CDevice::I2CIOError, e.message
|
49
|
+
end
|
50
|
+
|
51
|
+
# Interface of I2CDevice::Driver
|
52
|
+
def i2cset(address, *data)
|
53
|
+
i2c = File.open(@path, "r+")
|
54
|
+
i2c.ioctl(@slave_command, address)
|
55
|
+
i2c.syswrite(data.pack("C*"))
|
56
|
+
i2c.close
|
57
|
+
rescue Errno::EIO => e
|
58
|
+
raise I2CDevice::I2CIOError, e.message
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "../i2c"
|
4
|
+
|
5
|
+
module I2CDevice::Driver
|
6
|
+
# Abstract class for I2CDevice::Driver
|
7
|
+
class Base
|
8
|
+
include I2CDevice::Driver
|
9
|
+
end
|
10
|
+
|
11
|
+
# Low-level method for i2cget
|
12
|
+
# Driver must implement this.
|
13
|
+
# <tt>address</tt> :: [Integer] 7-bit slave address without r/w bit. MSB is always 0.
|
14
|
+
# <tt>data</tt> :: [Array[Integer]] Writing bytes array.
|
15
|
+
# Returns :: [String] Wrote bytes
|
16
|
+
def i2cget(address, param, length=1)
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
# Low-level method for i2cset
|
21
|
+
# Driver must implement this.
|
22
|
+
# <tt>address</tt> :: [Integer] 7-bit slave address without r/w bit. MSB is always 0.
|
23
|
+
# <tt>data</tt> :: [Array[Integer]] Writing bytes array.
|
24
|
+
# Returns :: [String] Wrote bytes
|
25
|
+
def i2cset(address, *data)
|
26
|
+
raise NotImplementedError
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Generic abstract class for I2C manipulation.
|
2
|
+
class I2CDevice
|
3
|
+
VERSION = "0.0.6"
|
4
|
+
|
5
|
+
# Super class of all of this library.
|
6
|
+
class I2CException < Exception; end
|
7
|
+
class I2CIOError < I2CException; end
|
8
|
+
class I2CBUSBusy < I2CIOError; end
|
9
|
+
|
10
|
+
# Slave address
|
11
|
+
attr_accessor :address
|
12
|
+
|
13
|
+
# <tt>args[:address]</tt> :: [Integer] 7-bit slave address without r/w bit. MSB is always 0.
|
14
|
+
# <tt>args[:driver]</tt> :: [I2CDevice::Driver::I2CDev] Instance of driver class
|
15
|
+
def initialize(args={})
|
16
|
+
if args[:driver].nil?
|
17
|
+
require "i2c/driver/i2c-dev"
|
18
|
+
args[:driver] = I2CDevice::Driver::I2CDev.new
|
19
|
+
end
|
20
|
+
|
21
|
+
@driver = args[:driver]
|
22
|
+
@address = args[:address] or raise I2CException, "args[:address] required"
|
23
|
+
end
|
24
|
+
|
25
|
+
# This method read data from slave with following process:
|
26
|
+
#
|
27
|
+
# 1. Write `param` to slave
|
28
|
+
# 2. re-start
|
29
|
+
# 3. Read data until NACK or `length`
|
30
|
+
#
|
31
|
+
# <tt>param</tt> :: [Integer] First writing byte. Typically, this is slave memory address.
|
32
|
+
# <tt>length=1</tt> :: [Integer] Read bytes length
|
33
|
+
# Returns :: [String] Bytes
|
34
|
+
def i2cget(param, length=1)
|
35
|
+
@driver.i2cget(@address, param, length)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Write _data_ to slave.
|
39
|
+
# <tt>data</tt> :: [Array[Integer]] Writing bytes array.
|
40
|
+
# Returns :: [String] Wrote bytes
|
41
|
+
def i2cset(*data)
|
42
|
+
@driver.i2cset(@address, *data)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: apiotics_factory
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Apiotics
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-05-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.20'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.20'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: httparty
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.14'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.14'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rest-client
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.0'
|
55
|
+
description: A gem to quickly create stub drivers for Apiotics devices.
|
56
|
+
email: info@apiotics.com
|
57
|
+
executables:
|
58
|
+
- apiotics_factory
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- License
|
63
|
+
- bin/apiotics_factory
|
64
|
+
- lib/apiotics_factory.rb
|
65
|
+
- lib/apiotics_factory/clear_credentials.rb
|
66
|
+
- lib/apiotics_factory/configuration.rb
|
67
|
+
- lib/apiotics_factory/generator.rb
|
68
|
+
- lib/apiotics_factory/my_command.rb
|
69
|
+
- lib/apiotics_factory/portal.rb
|
70
|
+
- lib/apiotics_factory/publish.rb
|
71
|
+
- lib/apiotics_factory/underscore.rb
|
72
|
+
- lib/apiotics_factory/version.rb
|
73
|
+
- lib/templates/apiotics_driver.rb.erb
|
74
|
+
- lib/templates/apiotics_interfaces.rb.erb
|
75
|
+
- lib/templates/config.json.erb
|
76
|
+
- lib/templates/exec.rb.erb
|
77
|
+
- lib/templates/grove_pi/grove_pi.rb
|
78
|
+
- lib/templates/grove_pi/i2c/device/acm1602ni.rb
|
79
|
+
- lib/templates/grove_pi/i2c/device/adt7410.rb
|
80
|
+
- lib/templates/grove_pi/i2c/device/aqm0802.rb
|
81
|
+
- lib/templates/grove_pi/i2c/device/bmp180.rb
|
82
|
+
- lib/templates/grove_pi/i2c/device/d6t-44l.rb
|
83
|
+
- lib/templates/grove_pi/i2c/device/hd44780.rb
|
84
|
+
- lib/templates/grove_pi/i2c/device/hdc1000.rb
|
85
|
+
- lib/templates/grove_pi/i2c/device/mpl115a2.rb
|
86
|
+
- lib/templates/grove_pi/i2c/driver.rb
|
87
|
+
- lib/templates/grove_pi/i2c/driver/gpio.rb
|
88
|
+
- lib/templates/grove_pi/i2c/driver/i2c-dev.rb
|
89
|
+
- lib/templates/grove_pi/i2c/i2c.rb
|
90
|
+
homepage: https://www.apiotics.com
|
91
|
+
licenses:
|
92
|
+
- Apiotics Factory License
|
93
|
+
metadata: {}
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options: []
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
requirements: []
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 2.2.2
|
111
|
+
signing_key:
|
112
|
+
specification_version: 4
|
113
|
+
summary: A Gem To Generate Apiotics Drivers
|
114
|
+
test_files: []
|