Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor device presentation #336

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/controllers/v0/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class ApplicationController < ActionController::API
helper ::UserHelper
include ::UserHelper

helper ::PresentationHelper
include ::PresentationHelper

respond_to :json

before_action :prepend_view_paths
Expand Down
2 changes: 0 additions & 2 deletions app/controllers/v0/me_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ def user_params
:country_code,
:profile_picture,
:url,
:avatar,
:avatar_url
)
end

Expand Down
1 change: 0 additions & 1 deletion app/controllers/v0/static_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ def search
h['url'] = v0_device_url(s.searchable_id)
elsif s.searchable_type == 'User'
h['username'] = s.searchable.username
h['avatar'] = s.searchable.avatar
h['profile_picture'] = profile_picture_url(s.searchable)
h['city'] = s.searchable.city
h['url'] = v0_user_url(s.searchable_id)
Expand Down
66 changes: 0 additions & 66 deletions app/controllers/v0/uploads_controller.rb

This file was deleted.

2 changes: 0 additions & 2 deletions app/controllers/v0/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ def user_params
:city,
:country_code,
:url,
:avatar,
:avatar_url
)
end

Expand Down
5 changes: 5 additions & 0 deletions app/helpers/presentation_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module PresentationHelper
def present(model, options={})
Presenters.present(model, current_user, self, options)
end
end
9 changes: 1 addition & 8 deletions app/jobs/mqtt_forwarding_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,7 @@ def perform(device_id, reading)
private

def payload_for(device, reading)
renderer.render(
partial: "v0/devices/device",
locals: {
device: device.reload,
current_user: nil,
slim_owner: true
}
)
Presenters.present(device, device.owner, renderer, readings: [reading])
end

def mqtt_client
Expand Down
26 changes: 26 additions & 0 deletions app/lib/presenters.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Presenters
# This is work in progress we're releasing early so
# that it can be used in forwarding to send the current
# values as they're received.
# TODO: add presenter tests, finish refactor following
# spec in your spreadsheet, remove unneeded options,
# use in appropriate views, add unauthorized_fields logic
# delete unneeded code in models and views.
PRESENTERS = {
Device => Presenters::DevicePresenter,
User => Presenters::UserPresenter,
Component => Presenters::ComponentPresenter,
Sensor => Presenters::SensorPresenter,
Measurement => Presenters::MeasurementPresenter,
}

def self.present(model_or_collection, user, render_context, options={})
if model_or_collection.is_a?(Enumerable)
model_or_collection.map { |model| present(model, user, render_context, options) }
else
PRESENTERS[model_or_collection.class]&.new(
model_or_collection, user, render_context, options
).as_json
end
end
end
42 changes: 42 additions & 0 deletions app/lib/presenters/base_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module Presenters
class BasePresenter

def default_options
{}
end

def exposed_fields
[]
end

def initialize(model, current_user=nil, render_context=nil, options={})
@model = model
@current_user = current_user
@render_context = render_context
@options = self.default_options.merge(options)
end

def as_json(_opts=nil)
self.exposed_fields.inject({}) { |hash, field|
value = self.send(field)
value.nil? ? hash : hash.merge(field => value)
}
end

def method_missing(method, *args, &block)
if self.exposed_fields.include?(method)
model.public_send(method, *args, &block)
else
super
end
end

def present(other_model, options={})
Presenters.present(other_model, current_user, render_context, options)
end

private

attr_reader :model, :current_user, :options, :render_context
end
end
46 changes: 46 additions & 0 deletions app/lib/presenters/component_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module Presenters
class ComponentPresenter < BasePresenter

alias_method :component, :model

def default_options
{ readings: nil }
end

def exposed_fields
%i{key sensor last_reading_at latest_value previous_value readings}
end

def sensor
present(component.sensor)
end

def latest_value
data = component.device.data
data[component.sensor_id.to_s] if data
end

def previous_value
old_data = component.device.old_data
old_data[component.sensor_id.to_s] if old_data
end

def readings
readings = options[:readings]
if readings
readings.map { |reading| format_reading(reading) }.compact
end
end

private

def format_reading(reading)
timestamp = reading[""]
value = reading[component.sensor_id.to_s]
raw_value = reading["#{component.sensor_id}_raw"]
if value || raw_value
{ timestamp: timestamp, value: value, raw_value: raw_value }.compact
end
end
end
end
94 changes: 94 additions & 0 deletions app/lib/presenters/device_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
module Presenters
class DevicePresenter < BasePresenter
alias_method :device, :model

def default_options
{
with_owner: true,
with_data: true,
with_postprocessing: true,
with_location: true,
slim_owner: false,
never_authorized: false,
readings: nil
}
end

def exposed_fields
%i{id uuid name description state system_tags user_tags last_reading_at created_at updated_at notify device_token mac_address postprocessing location data_policy hardware owner components}
end

def notify
{
stopped_publishing: device.notify_stopped_publishing,
low_battery: device.notify_low_battery
}
end

def location
if options[:with_location]
{
exposure: device.exposure,
elevation: device.elevation.try(:to_i) ,
latitude: device.latitude,
longitude: device.longitude,
geohash: device.geohash,
city: device.city,
country_code: device.country_code,
country: device.country_name
}
end
end

def data_policy
{
is_private: authorized? ? device.is_private : "[FILTERED]",
enable_forwarding: authorized? ? device.enable_forwarding : "[FILTERED]",
precise_location: authorized? ? device.precise_location : "[FILTERED]"
}
end

def hardware
{
name: device.hardware_name,
type: device.hardware_type,
version: device.hardware_version,
slug: device.hardware_slug,
last_status_message: authorized? ? device.hardware_info : "[FILTERED]",
}
end

def owner
if options[:with_owner] && device.owner
present(device.owner, with_devices: false)
end
end

def postprocessing
device.postprocessing if options[:with_postprocessing]
end

def device_token
authorized? ? device.device_token : "[FILTERED]"
end

def mac_address
authorized? ? device.mac_address : "[FILTERED]"
end

def components
present(device.components)
end

private

def authorized?
!options[:never_authorized] && policy.show_private_info?
end

def policy
@policy ||= DevicePolicy.new(current_user, device)
end

end
end
10 changes: 10 additions & 0 deletions app/lib/presenters/measurement_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Presenters
class MeasurementPresenter < BasePresenter

alias_method :measurement, :model

def exposed_fields
%i{id name description unit uuid definition}
end
end
end
14 changes: 14 additions & 0 deletions app/lib/presenters/sensor_presenter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Presenters
class SensorPresenter < BasePresenter

alias_method :sensor, :model

def exposed_fields
%i{id parent_id name description unit created_at updated_at uuid default_key datasheet unit_definition measurement tags}
end

def measurement
present(sensor.measurement)
end
end
end
Loading
Loading