Skip to content

Commit

Permalink
Move extra functionalities to optional installer
Browse files Browse the repository at this point in the history
  • Loading branch information
Matteo La Cognata [fabbricadigitale] committed Aug 4, 2018
1 parent d3237a3 commit 7f9b80d
Show file tree
Hide file tree
Showing 31 changed files with 811 additions and 25 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ gem install \

```
rails new myapi \
-m https://raw.github.com/matteolc/rails-api-template/master/generator.rb \
-m https://raw.github.com/matteolc/rails-api-template/master/template.rb \
-d postgresql \
--api
```
Expand Down
57 changes: 57 additions & 0 deletions app/controllers/api/v1/excel_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

class Api::V1::ExcelController < ApplicationController
before_action :authenticate_user!

include ActionController::MimeResponds

def download
records = related ?
instance.send(related)
: klass_search_scope.where(allowed_filter)
send_data(records.to_xlsx)
rescue StandardError => e
render_error e.message
end

private

# params[:resource] the model name we want to download records from
def klass
params[:resource].tableize.singularize.camelize.constantize
end

# filter[:q] is a free-text search
def klass_search_scope
filter[:q].present? ?
klass.search(filter[:q])
: klass.all
end

# params[:related] controls whether to send
# false: noop
# true: all records related to the id of the object received
# ex. all posts of a certain author
def related
params[:related] ?
params[:related].tableize : nil
end

# params[:id] required when requesting related records
def instance
params[:id] ?
klass.find(params[:id])
: nil
end

# params[:filter] filters applied to the search records
def filter
params[:filter] && params[:filter].permit! || {}
end

def allowed_filter
filter.except(:q)
end

end

27 changes: 27 additions & 0 deletions app/controllers/api/v1/pdf_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

class Api::V1::PdfController < ApplicationController
before_action :authenticate_user!

include ActionController::MimeResponds

def print
send_file(
instance.to_pdf,
filename: instance.name,
type: :pdf )
rescue StandardError => e
render_error e.message
end

private

def klass
params[:resource].singularize.camelize.constantize
end

def instance
klass.find(params[:id])
end

end
12 changes: 8 additions & 4 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
class ApplicationController < ActionController::API

include Authorization


def not_found
render_error 'Page not found', 404
end

protected

def render_error(message, status: :bad_request)
Rails.logger.warn { message }
render json: { errors: [{ detail: message }], status: status }, status: status
def render_error(message, status)
Rails.logger.warn { message }
render json: { errors: [{ detail: message }] }, status: status || :bad_request
end

end
7 changes: 7 additions & 0 deletions app/jobs/application_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class ApplicationJob < ActiveJob::Base
discard_on(StandardError) do |job, exception|
puts(exception)
end
end
5 changes: 5 additions & 0 deletions app/mailers/application_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

class ApplicationMailer < ActionMailer::Base
layout 'mailer'
end
24 changes: 24 additions & 0 deletions app/models/concerns/acts_as_spreadsheet/spreadsheet.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

require 'roo'
require 'roo-xls'

module ActsAsSpreadsheet
module Spreadsheet
extend self

def open(file_path)
open_spreadsheet(File.open(file_path))
end

def open_spreadsheet(file)
case File.extname(file)
when '.csv' then Roo::CSV.new(file.path, csv_options: { col_sep: ';' })
when '.ods' then Roo::OpenOffice.new(file.path)
when '.xls' then Roo::Excel.new(file.path)
when '.xlsx' then Roo::Excelx.new(file.path)
else raise "Unknown file type: #{file}"
end
end
end
end
29 changes: 29 additions & 0 deletions app/models/concerns/has_exchange_rate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module HasExchangeRate
extend ActiveSupport::Concern

class_methods do
def has_exchange_rate(options = {}); end
end

included do

default_value_for :auto_update_exchange_rate,
true

default_value_for :currency_code, ENV['CURRENCY']

validates :exchange_rate,
presence: true,
numericality: true,
unless: proc { |r| r.auto_update_exchange_rate? }
end

def effective_exchange_rate
auto_update_exchange_rate ?
(ENV['CURRENCY'] === currency_code ? 1 : OpenExchangeRate.get_exchange_rate(currency_code, ENV['CURRENCY'])) : exchange_rate
end

end

92 changes: 92 additions & 0 deletions app/models/country.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# frozen_string_literal: true
# ## Schema Information
#
# Table name: `countries`
#
# ### Columns
#
# Name | Type | Attributes
# --------------------------- | ------------------ | ---------------------------
# **`id`** | `uuid` | `not null, primary key`
# **`name`** | `citext` | `not null`
# **`alpha2`** | `string` |
# **`alpha3`** | `string` | `not null`
# **`region`** | `string` |
# **`subregion`** | `string` |
# **`world_region`** | `string` |
# **`country_code`** | `string` |
# **`international_prefix`** | `string` |
# **`currency_code`** | `string` |
# **`currency_name`** | `string` |
# **`currency_symbol`** | `string` |
# **`timezones`** | `string` | `is an Array`
# **`timezones_offsets`** | `string` | `is an Array`
# **`created_at`** | `datetime` | `not null`
# **`updated_at`** | `datetime` | `not null`
#
# ### Indexes
#
# * `index_countries_on_name` (_unique_):
# * **`name`**
#

class Country < ApplicationRecord
include HasFulltextSearch
include SpreadsheetArchitect

validates_uniqueness_of :name,
case_sensitive: false

validates :alpha3,
inclusion: { in: ISO3166::Country.all.map(&:alpha3) },
presence: true

def iso3166_country
ISO3166::Country.find_country_by_alpha3(alpha3)
end

def tz_info
TZInfo::Country.get(iso3166_country.alpha2)
end

before_save :set_or_update_country_info
def set_or_update_country_info
self.alpha2 = iso3166_country.alpha2
self.region = iso3166_country.continent
self.subregion = iso3166_country.subregion
self.world_region = iso3166_country.world_region
self.international_prefix = iso3166_country.international_prefix
self.country_code = iso3166_country.country_code
end

before_save :set_or_update_currency_info
def set_or_update_currency_info
return if iso3166_country.currency.nil?
self.currency_code = iso3166_country.currency.iso_code
self.currency_symbol = iso3166_country.currency.symbol
self.currency_name = iso3166_country.currency.name
end

before_save :set_or_update_timezones_info
def set_or_update_timezones_info
timezones_offsets = []
tz_info.zone_identifiers.each do |time_zone|
timezones_offsets.push(Time.now.in_time_zone(time_zone).formatted_offset)
end
self.timezones = tz_info.zone_identifiers
self.timezones_offsets = timezones_offsets
rescue StandardError # TZInfo::InvalidCountryCode: Invalid country code
end

after_commit :flush_cache, on: [:update, :destroy]

def flush_cache
Rails.cache.delete(["Country", self.id])
end

def self.find_in_cache(id)
Rails.cache.fetch(["Country", id], expires_in: 1.day) do find(id) end
end

end

43 changes: 43 additions & 0 deletions app/models/open_exchange_rate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

require 'money/bank/open_exchange_rates_bank'

class OpenExchangeRate
def initialize
@moe = Money::Bank::OpenExchangeRatesBank.new
@moe.cache = 'tmp/rates'
@moe.app_id = ENV['OPEN_EXCHANGE_RATE_SECRET']
end

def update_rates
@moe.update_rates
@moe.save_rates
Money.default_bank = @moe
end

def self.update_rates
new.update_rates if Money.default_bank.rates.empty?
end

def self.exists_exchange_rate_for_currency?(currency_code)
result = get_exchange_rate_usd(currency_code)
return false if result.nil? || result == 'Unknown Currency'
true
end

def self.get_exchange_rate_usd(to_currency_code)
return 'Unknown Currency' unless Money::Currency.table.map { |t| t[1][:iso_code] }.include?(to_currency_code)
update_rates
Money.default_bank.get_rate 'USD',
to_currency_code
end

def self.get_exchange_rate(from_currency_code, to_currency_code)
exchange_rate_usd = get_exchange_rate_usd(to_currency_code)
return exchange_rate_usd if from_currency_code === 'USD'
exchange_rate_from = get_exchange_rate_usd(from_currency_code)
return nil unless exchange_rate_from || exchange_rate_from == 'Unknown Currency'
exchange_rate_usd / exchange_rate_from
end

end
11 changes: 6 additions & 5 deletions app/resources/api/v1/user_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

class Api::V1::UserResource < Api::V1::ApiResource

VIRTUAL_ATTRIBUTES = %i[password_confirmation roles].freeze

attributes *User.attribute_names.map(&:to_sym) -
%i[id] +
VIRTUAL_ATTRIBUTES
begin
attributes *User.attribute_names.map(&:to_sym) -
%i[id] +
%i[password password_confirmation roles]
rescue
end

def self.fields
super - [:password, :password_confirmation]
Expand Down
9 changes: 9 additions & 0 deletions app/views/layouts/mailer.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<%= yield %>
</body>
</html>
1 change: 1 addition & 0 deletions app/views/layouts/mailer.text.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= yield %>
Loading

0 comments on commit 7f9b80d

Please sign in to comment.