extface 0.0.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +34 -0
- data/app/assets/javascripts/extface/application.js +13 -0
- data/app/assets/javascripts/extface/devices.js +2 -0
- data/app/assets/javascripts/extface/jobs.js +17 -0
- data/app/assets/stylesheets/extface/application.css +13 -0
- data/app/assets/stylesheets/extface/devices.css +4 -0
- data/app/assets/stylesheets/extface/jobs.css +4 -0
- data/app/assets/stylesheets/extface/sse.css +4 -0
- data/app/assets/stylesheets/scaffold.css +56 -0
- data/app/controllers/extface/application_controller.rb +21 -0
- data/app/controllers/extface/devices_controller.rb +69 -0
- data/app/controllers/extface/handler_controller.rb +66 -0
- data/app/controllers/extface/jobs_controller.rb +30 -0
- data/app/helpers/extface/application_helper.rb +40 -0
- data/app/helpers/extface/devices_helper.rb +21 -0
- data/app/helpers/extface/jobs_helper.rb +4 -0
- data/app/helpers/extface/sse_helper.rb +4 -0
- data/app/models/extface/device.rb +47 -0
- data/app/models/extface/driver/datecs_fp550.rb +6 -0
- data/app/models/extface/driver/fixed_width_serial_cdr.rb +6 -0
- data/app/models/extface/driver/generic_pos.rb +7 -0
- data/app/models/extface/driver/raw_serial.rb +27 -0
- data/app/models/extface/driver/star_scp700.rb +16 -0
- data/app/models/extface/driver/star_tsp200.rb +6 -0
- data/app/models/extface/driver_base.rb +67 -0
- data/app/models/extface/fiscal_print_driver.rb +8 -0
- data/app/models/extface/job.rb +54 -0
- data/app/models/extface/pbx_cdr_driver.rb +12 -0
- data/app/models/extface/pos_print_driver.rb +45 -0
- data/app/models/extface/raw_driver.rb +16 -0
- data/app/models/extface/serial_config.rb +10 -0
- data/app/views/extface/application/_object_errors.html.erb +10 -0
- data/app/views/extface/devices/form.html.erb +46 -0
- data/app/views/extface/devices/index.html.erb +36 -0
- data/app/views/extface/devices/show.html.erb +56 -0
- data/app/views/extface/driver/fixed_width_serial_cdr/_settings.html.erb +0 -0
- data/app/views/extface/driver/star_scp700/_settings.html.erb +1 -0
- data/app/views/extface/jobs/_form.html.erb +21 -0
- data/app/views/extface/jobs/edit.html.erb +6 -0
- data/app/views/extface/jobs/index.html.erb +27 -0
- data/app/views/extface/jobs/new.html.erb +5 -0
- data/app/views/extface/jobs/show.html.erb +9 -0
- data/app/views/extface/serial_configs/_settings.html.erb +37 -0
- data/app/views/layouts/extface/application.html.erb +14 -0
- data/config/routes.rb +12 -0
- data/db/migrate/20140215192111_create_extface_devices.rb +12 -0
- data/db/migrate/20140216110018_create_extface_pos_print_drivers.rb +9 -0
- data/db/migrate/20140216110608_create_extface_serial_configs.rb +14 -0
- data/db/migrate/20140216131245_create_extface_raw_drivers.rb +9 -0
- data/db/migrate/20140216131457_create_extface_fiscal_print_drivers.rb +9 -0
- data/db/migrate/20140216134748_create_extface_pbx_cdr_drivers.rb +9 -0
- data/db/migrate/20140219091811_create_extface_jobs.rb +9 -0
- data/db/migrate/20140219175006_add_job_info_to_extface_jobs.rb +9 -0
- data/lib/extface.rb +34 -0
- data/lib/extface/engine.rb +17 -0
- data/lib/extface/extfaceable.rb +17 -0
- data/lib/extface/mapping.rb +31 -0
- data/lib/extface/routes.rb +8 -0
- data/lib/extface/version.rb +3 -0
- data/lib/tasks/extface_tasks.rake +4 -0
- data/test/controllers/extface/devices_controller_test.rb +51 -0
- data/test/controllers/extface/jobs_controller_test.rb +14 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/shop.rb +3 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +24 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +7 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20140221203517_create_shops.rb +8 -0
- data/test/dummy/db/schema.rb +83 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +57 -0
- data/test/dummy/log/test.log +5908 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/0a8a23b1a73ebf217f4e2b765013ce7b +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/21fb58a50e25d1170f2c7d5bd5d29c54 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/263375f2d55c31687ed052f99ddead55 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/3f4ff460a79b2e29781690193a3960f6 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/4ef10b222fde3c042209a46daf3d3e36 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/544650645bc510ef8fbee0043c41fdcf +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/6dbf2b0443792e51055710be15c83197 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/717acb3e7c0df18696cf1bc56de6d3d5 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/9891cbb0808f38b2c42f42fd89e662a4 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/9b178820b9dc8c81c9784cf7ee3b7d35 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/a06a4a7f51754a6a2c0694eb39ff824b +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/a8cc2e73253025d2518d2449e16e5266 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/b56dfad0b14283f2cc10c62f37d71cc7 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/e1197e0129ade843b08de0b45c47fc40 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/ece3270b150db7c668579bb91ac7e2e5 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/f018ab87f3490dbbcbc2e3c9f36aed3d +0 -0
- data/test/extface_test.rb +7 -0
- data/test/fixtures/extface/devices.yml +13 -0
- data/test/fixtures/extface/fiscal_print_drivers.yml +7 -0
- data/test/fixtures/extface/jobs.yml +7 -0
- data/test/fixtures/extface/pbx_cdr_drivers.yml +7 -0
- data/test/fixtures/extface/pos_print_drivers.yml +7 -0
- data/test/fixtures/extface/raw_drivers.yml +7 -0
- data/test/fixtures/extface/serial_configs.yml +17 -0
- data/test/fixtures/shops.yml +11 -0
- data/test/helpers/extface/devices_helper_test.rb +6 -0
- data/test/helpers/extface/jobs_helper_test.rb +6 -0
- data/test/helpers/extface/sse_helper_test.rb +6 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/models/extface/device_test.rb +9 -0
- data/test/models/extface/driver/star_tsp200_test.rb +9 -0
- data/test/models/extface/driver_test.rb +9 -0
- data/test/models/extface/fiscal_print_driver_test.rb +9 -0
- data/test/models/extface/job_test.rb +9 -0
- data/test/models/extface/pbx_cdr_driver_test.rb +9 -0
- data/test/models/extface/pos_print_driver_test.rb +9 -0
- data/test/models/extface/raw_driver_test.rb +9 -0
- data/test/models/extface/serial_config_test.rb +9 -0
- data/test/test_helper.rb +29 -0
- metadata +322 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module Extface
|
2
|
+
class Device < ActiveRecord::Base
|
3
|
+
attr_writer :driver
|
4
|
+
belongs_to :extfaceable, polymorphic: true
|
5
|
+
belongs_to :driveable, polymorphic: true
|
6
|
+
has_many :jobs, inverse_of: :device
|
7
|
+
|
8
|
+
accepts_nested_attributes_for :driveable
|
9
|
+
|
10
|
+
delegate :print?, :fiscal?, :raw?, :cdr?, to: :driveable, allow_nil: true
|
11
|
+
|
12
|
+
validates_uniqueness_of :name, :uuid, scope: [:extfaceable_id, :extfaceable_type]
|
13
|
+
|
14
|
+
before_create do
|
15
|
+
self.uuid = SecureRandom.hex
|
16
|
+
self.name = uuid unless name.present?
|
17
|
+
self.driveable = @driver.constantize.create if @driver.present?
|
18
|
+
end
|
19
|
+
|
20
|
+
def driver
|
21
|
+
driveable.try(:class)
|
22
|
+
end
|
23
|
+
|
24
|
+
def driver_name
|
25
|
+
driver::NAME if driver
|
26
|
+
end
|
27
|
+
|
28
|
+
def session(description = nil)
|
29
|
+
job = jobs.create!(description: description)
|
30
|
+
Thread.new do
|
31
|
+
begin
|
32
|
+
raise 'No driver configured' unless driveable.present?
|
33
|
+
driveable.set_job(job)
|
34
|
+
yield driveable
|
35
|
+
job.complete!
|
36
|
+
rescue => e
|
37
|
+
STDERR.puts e.message
|
38
|
+
e.backtrace.each do |line|
|
39
|
+
p line
|
40
|
+
end
|
41
|
+
job.failed! e.message
|
42
|
+
end
|
43
|
+
end
|
44
|
+
job
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Extface
|
2
|
+
class Driver::RawSerial < Extface::RawDriver
|
3
|
+
NAME = 'RAW Serial'.freeze
|
4
|
+
has_serial_config
|
5
|
+
|
6
|
+
def push(buffer)
|
7
|
+
Extface.redis.rpush(:key, buffer)
|
8
|
+
Extface.redis.subscribe(:extface) do |on|
|
9
|
+
on.message do |event, data|
|
10
|
+
@return = "event: #{event} data: #{data}\n\n"
|
11
|
+
Extface.redis.unsubscribe
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def receive(timeout = nil)
|
17
|
+
list, element = Extface.redis.blpop(:key, :timeout => timeout)
|
18
|
+
# Extface.redis.subscribe(:extface) do |on|
|
19
|
+
# on.message do |event, data|
|
20
|
+
# @return = "event: #{event} data: #{data}\n\n"
|
21
|
+
# Extface.redis.unsubscribe
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
return element
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Extface
|
2
|
+
class Driver::StarScp700 < Extface::Driver::StarTsp200
|
3
|
+
NAME = 'Star SCP700 (Receipt only)'.freeze
|
4
|
+
|
5
|
+
alias_method :print, :push
|
6
|
+
|
7
|
+
def autocut(partial = true)
|
8
|
+
print "\r\n\r\n\r\n"
|
9
|
+
print partial ? "\x1B\x64\x31" : "\x1B\x64\x30"
|
10
|
+
end
|
11
|
+
|
12
|
+
def autocutter?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require'timeout'
|
2
|
+
module Extface
|
3
|
+
class DriverBase < ActiveRecord::Base
|
4
|
+
self.abstract_class = true
|
5
|
+
|
6
|
+
has_one :device, :as => :driveable
|
7
|
+
|
8
|
+
[:raw?, :print?, :fiscal?, :cdr?].each do |driver_type|
|
9
|
+
define_method driver_type do
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def has_serial_config
|
16
|
+
has_one :serial_config, as: :s_configureable
|
17
|
+
accepts_nested_attributes_for :serial_config
|
18
|
+
define_method :serial? do
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle(buffer) # handle push messages from device outside active session
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
def push(buffer)
|
29
|
+
|
30
|
+
if @job
|
31
|
+
Timeout.timeout(10) do
|
32
|
+
Extface.redis_block do |r|
|
33
|
+
r.subscribe(@job.id) do |on| #blocking until delivered
|
34
|
+
on.subscribe do |channel, subscriptions|
|
35
|
+
@job.rpush buffer
|
36
|
+
end
|
37
|
+
on.message do |event, data|
|
38
|
+
r.unsubscribe
|
39
|
+
@job.connected!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
else
|
45
|
+
raise "No job given"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def pull(timeout = nil)
|
51
|
+
Extface.redis_block do |r|
|
52
|
+
list, element = r.blpop(device.uuid, :timeout => timeout)
|
53
|
+
end
|
54
|
+
element
|
55
|
+
end
|
56
|
+
|
57
|
+
def notify(message)
|
58
|
+
raise "No job given" unless @job
|
59
|
+
@job.notify(message)
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_job(job)
|
63
|
+
@job = job
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Extface
|
2
|
+
class Job < ActiveRecord::Base
|
3
|
+
belongs_to :device, inverse_of: :jobs
|
4
|
+
|
5
|
+
scope :active, ->{ where(arel_table[:completed_at].eq(nil).and(arel_table[:failed_at].eq(nil))) }
|
6
|
+
|
7
|
+
def complete!
|
8
|
+
self.completed_at = Time.now
|
9
|
+
save!
|
10
|
+
notify("Job #{self.id} completed!")
|
11
|
+
end
|
12
|
+
|
13
|
+
def completed?
|
14
|
+
!!completed_at
|
15
|
+
end
|
16
|
+
|
17
|
+
def connected!
|
18
|
+
unless connected?
|
19
|
+
self.connected_at = Time.now
|
20
|
+
save!
|
21
|
+
notify("Job #{self.id} device connected!")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def connected?
|
26
|
+
!!connected_at?
|
27
|
+
end
|
28
|
+
|
29
|
+
def failed!(message)
|
30
|
+
self.error = message
|
31
|
+
self.failed_at = Time.now
|
32
|
+
save!
|
33
|
+
notify(message)
|
34
|
+
notify("Job #{self.id} failed!")
|
35
|
+
end
|
36
|
+
|
37
|
+
def failed?
|
38
|
+
!!failed_at
|
39
|
+
end
|
40
|
+
|
41
|
+
def notify(message)
|
42
|
+
Extface.redis_block do |r|
|
43
|
+
r.publish(self.id, message)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def rpush(buffer)
|
48
|
+
Extface.redis_block do |r|
|
49
|
+
r.rpush self.id, buffer
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Extface
|
2
|
+
class PosPrintDriver < Extface::DriverBase
|
3
|
+
GROUP = 'POS Printers'.freeze
|
4
|
+
def print(buffer)
|
5
|
+
raise "Not Impelmented"
|
6
|
+
end
|
7
|
+
|
8
|
+
def print_test_page
|
9
|
+
device.session("Print Test Page") do |s|
|
10
|
+
sleep 1
|
11
|
+
s.notify "Printing Test Page"
|
12
|
+
s.print "******************************\r\n"
|
13
|
+
s.print "* Extface Print Test Page *\r\n"
|
14
|
+
s.print "******************************\r\n"
|
15
|
+
|
16
|
+
s.notify "Printing driver information"
|
17
|
+
s.print "\r\nDriver:\r\n"
|
18
|
+
s.print "------------------------------\r\n"
|
19
|
+
s.print "#{self.class::NAME}".truncate(30)
|
20
|
+
s.print "\r\n"
|
21
|
+
|
22
|
+
if try(:serial?)
|
23
|
+
s.notify "Printing serial settings"
|
24
|
+
s.print "\r\nSerial Port Settings:\r\n"
|
25
|
+
s.print "------------------------------\r\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
s.print "\r\n"
|
29
|
+
s.print "------------------------------\r\n"
|
30
|
+
s.print Time.now.strftime("Printed on %m/%d/%Y %T\r\n").rjust(32)
|
31
|
+
s.print "\r\n\r\n"
|
32
|
+
s.notify "Printing finished"
|
33
|
+
|
34
|
+
if s.try(:autocutter?)
|
35
|
+
s.autocut
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def print?
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Extface
|
2
|
+
class SerialConfig < ActiveRecord::Base
|
3
|
+
BOUD_RATES = [2400, 4800, 9600, 19200].freeze
|
4
|
+
DATA_LENGTHS = [7, 8].freeze
|
5
|
+
PARITY_CHECKS = [0, 1, 2].freeze
|
6
|
+
STOP_BITS = [1, 2].freeze
|
7
|
+
HANDSHAKE = [0, 1, 2].freeze
|
8
|
+
belongs_to :s_configureable, polymorphic: true
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<%= form_for @device, html: { class: 'form-horizontal' } do |f| %>
|
2
|
+
<div class="page-header nav">
|
3
|
+
<%= f.button type: :submit, name: :submit, value: :submit, class: 'btn btn-primary navbar-btn pull-right' do %>
|
4
|
+
✓ <%= t( @device.new_record? ? '.create' : '.update' ) %>
|
5
|
+
<% end %>
|
6
|
+
<h3 class='navbar-text'>
|
7
|
+
<%= link_to extface.root_path do %>
|
8
|
+
Extface<sup>♥</sup>
|
9
|
+
<% end %>
|
10
|
+
»
|
11
|
+
<%= link_to t('.devices'), devices_path %>
|
12
|
+
»
|
13
|
+
<%= t( @device.new_record? ? '.new' : '.edit') %>
|
14
|
+
</h3>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<%= render 'object_errors', object: f.object %>
|
18
|
+
|
19
|
+
<%= form_group f, :name, :text_field %>
|
20
|
+
|
21
|
+
<div class="form-group">
|
22
|
+
<%= f.label :driver, class: 'col-sm-2 control-label' %>
|
23
|
+
<div class='col-sm-10 col-md-8'>
|
24
|
+
<% if @device.driveable.present? %>
|
25
|
+
<p class="form-control-static">
|
26
|
+
<%= link_to '#', class: 'btn btn-danger btn-sm pull-right' do %>
|
27
|
+
<%= t('.change_driver') %>
|
28
|
+
<% end %>
|
29
|
+
<%= @device.driveable.class::NAME %>
|
30
|
+
</p>
|
31
|
+
<% else %>
|
32
|
+
<%= f.select :driver, options_for_drivers, { include_blank: true }, class: 'form-control' %>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
<%= fields_for :driveable do |fd| %>
|
37
|
+
<%= driver_settings fd, @device.driveable %>
|
38
|
+
<%= fields_for :serial_config do |fds| %>
|
39
|
+
<div class="form-group">
|
40
|
+
<div class='col-sm-offset-2 col-sm-10 col-md-8'>
|
41
|
+
<%= render 'extface/serial_configs/settings', f: fds %>
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
<% end if @device.driveable.serial? %>
|
45
|
+
<% end if @device.driveable.present? %>
|
46
|
+
<% end %>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<div class="page-header nav">
|
2
|
+
<%= link_to new_device_path, class: 'btn btn-default navbar-btn pull-right' do %>
|
3
|
+
+ <%= t('.new') %>
|
4
|
+
<% end %>
|
5
|
+
<h3 class='navbar-text'>
|
6
|
+
<%= link_to extface.root_path do %>
|
7
|
+
Extface<sup>♥</sup>
|
8
|
+
<% end %>
|
9
|
+
»
|
10
|
+
<%= t('.devices') %>
|
11
|
+
</h3>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<table class="table table-condensed table-stripped">
|
15
|
+
<thead>
|
16
|
+
<tr class='active'>
|
17
|
+
<th><%= t('.name') %></th>
|
18
|
+
<th><%= t('.parent') %></th>
|
19
|
+
<th><%= t('.driver') %></th>
|
20
|
+
<th> </th>
|
21
|
+
<th> </th>
|
22
|
+
</tr>
|
23
|
+
</thead>
|
24
|
+
<tbody>
|
25
|
+
<% @devices.each do |device| %>
|
26
|
+
<tr>
|
27
|
+
<td><%= link_to device.name, device %></td>
|
28
|
+
<td><%= device.extfaceable.composite_id %></td>
|
29
|
+
<td><%= device.driveable ? device.driveable.class::NAME : 'N/A' %></td>
|
30
|
+
<td class='text-right'><%= link_to 'Edit', edit_device_path(device), class: 'btn btn-primary btn-sm' %></td>
|
31
|
+
<td class='text-right'><%= button_to 'Delete', device, method: :delete, class: 'btn btn-danger btn-sm', data: { confirm: 'Are you sure?' } %></td>
|
32
|
+
</tr>
|
33
|
+
<% end %>
|
34
|
+
</tbody>
|
35
|
+
</table>
|
36
|
+
|