fmrest 0.6.0 → 0.10.0
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 +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +175 -40
- data/lib/fmrest/spyke.rb +1 -1
- data/lib/fmrest/spyke/base.rb +2 -0
- data/lib/fmrest/spyke/model.rb +2 -0
- data/lib/fmrest/spyke/model/associations.rb +2 -2
- data/lib/fmrest/spyke/model/connection.rb +30 -11
- data/lib/fmrest/spyke/model/global_fields.rb +40 -0
- data/lib/fmrest/spyke/model/orm.rb +2 -1
- data/lib/fmrest/spyke/model/serialization.rb +16 -5
- data/lib/fmrest/spyke/relation.rb +73 -0
- data/lib/fmrest/spyke/{json_parser.rb → spyke_formatter.rb} +50 -17
- data/lib/fmrest/string_date.rb +220 -0
- data/lib/fmrest/v1.rb +6 -0
- data/lib/fmrest/v1/connection.rb +8 -7
- data/lib/fmrest/v1/dates.rb +81 -0
- data/lib/fmrest/v1/token_session.rb +2 -0
- data/lib/fmrest/v1/type_coercer.rb +192 -0
- data/lib/fmrest/v1/utils.rb +1 -0
- data/lib/fmrest/version.rb +1 -1
- metadata +7 -4
- data/CODE_OF_CONDUCT.md +0 -74
data/lib/fmrest/v1.rb
CHANGED
@@ -4,12 +4,18 @@ require "fmrest/v1/connection"
|
|
4
4
|
require "fmrest/v1/paths"
|
5
5
|
require "fmrest/v1/container_fields"
|
6
6
|
require "fmrest/v1/utils"
|
7
|
+
require "fmrest/v1/dates"
|
7
8
|
|
8
9
|
module FmRest
|
9
10
|
module V1
|
11
|
+
DEFAULT_DATE_FORMAT = "MM/dd/yyyy"
|
12
|
+
DEFAULT_TIME_FORMAT = "HH:mm:ss"
|
13
|
+
DEFAULT_TIMESTAMP_FORMAT = "#{DEFAULT_DATE_FORMAT} #{DEFAULT_TIME_FORMAT}"
|
14
|
+
|
10
15
|
extend Connection
|
11
16
|
extend Paths
|
12
17
|
extend ContainerFields
|
13
18
|
extend Utils
|
19
|
+
extend Dates
|
14
20
|
end
|
15
21
|
end
|
data/lib/fmrest/v1/connection.rb
CHANGED
@@ -5,7 +5,7 @@ require "uri"
|
|
5
5
|
module FmRest
|
6
6
|
module V1
|
7
7
|
module Connection
|
8
|
-
BASE_PATH = "/fmi/data/v1/databases"
|
8
|
+
BASE_PATH = "/fmi/data/v1/databases"
|
9
9
|
|
10
10
|
# Builds a complete DAPI Faraday connection with middleware already
|
11
11
|
# configured to handle authentication, JSON parsing, logging and DAPI
|
@@ -19,7 +19,6 @@ module FmRest
|
|
19
19
|
# @option (see #base_connection)
|
20
20
|
# @return (see #base_connection)
|
21
21
|
def build_connection(options = FmRest.default_connection_settings, &block)
|
22
|
-
|
23
22
|
base_connection(options) do |conn|
|
24
23
|
conn.use RaiseErrors
|
25
24
|
conn.use TokenSession, options
|
@@ -32,17 +31,18 @@ module FmRest
|
|
32
31
|
conn.request :multipart
|
33
32
|
conn.request :json
|
34
33
|
|
35
|
-
if options[:log]
|
36
|
-
conn.response :logger, nil, bodies: true, headers: true
|
37
|
-
end
|
38
|
-
|
39
34
|
# Allow overriding the default response middleware
|
40
35
|
if block_given?
|
41
|
-
yield conn
|
36
|
+
yield conn, options
|
42
37
|
else
|
38
|
+
conn.use TypeCoercer, options
|
43
39
|
conn.response :json
|
44
40
|
end
|
45
41
|
|
42
|
+
if options[:log]
|
43
|
+
conn.response :logger, nil, bodies: true, headers: true
|
44
|
+
end
|
45
|
+
|
46
46
|
conn.adapter Faraday.default_adapter
|
47
47
|
end
|
48
48
|
end
|
@@ -86,3 +86,4 @@ end
|
|
86
86
|
|
87
87
|
require "fmrest/v1/token_session"
|
88
88
|
require "fmrest/v1/raise_errors"
|
89
|
+
require "fmrest/v1/type_coercer"
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FmRest
|
4
|
+
module V1
|
5
|
+
module Dates
|
6
|
+
FM_DATETIME_FORMAT_MATCHER = /MM|mm|dd|HH|ss|yyyy/.freeze
|
7
|
+
|
8
|
+
FM_DATE_TO_STRPTIME_SUBSTITUTIONS = {
|
9
|
+
"MM" => "%m",
|
10
|
+
"dd" => "%d",
|
11
|
+
"yyyy" => "%Y",
|
12
|
+
"HH" => "%H",
|
13
|
+
"mm" => "%M",
|
14
|
+
"ss" => "%S"
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
FM_DATE_TO_REGEXP_SUBSTITUTIONS = {
|
18
|
+
"MM" => '(?:0[1-9]|1[012])',
|
19
|
+
"dd" => '(?:0[1-9]|[12][0-9]|3[01])',
|
20
|
+
"yyyy" => '\d{4}',
|
21
|
+
"HH" => '(?:[01]\d|2[0123])',
|
22
|
+
"mm" => '[0-5]\d',
|
23
|
+
"ss" => '[0-5]\d'
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
def self.extended(mod)
|
27
|
+
mod.instance_eval do
|
28
|
+
@date_strptime = {}
|
29
|
+
@date_regexp = {}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Converts a FM date-time format to `DateTime.strptime` format
|
34
|
+
#
|
35
|
+
# @param fm_format [String] The FileMaker date-time format
|
36
|
+
# @return [String] The `DateTime.strpdate` equivalent of the given FM
|
37
|
+
# date-time format
|
38
|
+
def fm_date_to_strptime_format(fm_format)
|
39
|
+
@date_strptime[fm_format] ||=
|
40
|
+
fm_format.gsub(FM_DATETIME_FORMAT_MATCHER, FM_DATE_TO_STRPTIME_SUBSTITUTIONS).freeze
|
41
|
+
end
|
42
|
+
|
43
|
+
# Converts a FM date-time format to a Regexp. This is mostly used a
|
44
|
+
# quicker way of checking whether a FM field is a date field than
|
45
|
+
# Date|DateTime.strptime
|
46
|
+
#
|
47
|
+
# @param fm_format [String] The FileMaker date-time format
|
48
|
+
# @return [Regexp] A reegular expression matching strings in the given FM
|
49
|
+
# date-time format
|
50
|
+
def fm_date_to_regexp(fm_format)
|
51
|
+
@date_regexp[fm_format] ||=
|
52
|
+
Regexp.new('\A' + fm_format.gsub(FM_DATETIME_FORMAT_MATCHER, FM_DATE_TO_REGEXP_SUBSTITUTIONS) + '\Z').freeze
|
53
|
+
end
|
54
|
+
|
55
|
+
# Takes a DateTime dt, and returns the correct local offset for that dt,
|
56
|
+
# daylight savings included, in fraction of a day.
|
57
|
+
#
|
58
|
+
# By default, if ActiveSupport's Time.zone is set it will be used instead
|
59
|
+
# of the system timezone.
|
60
|
+
#
|
61
|
+
# @param dt [DateTime] The DateTime to get the offset for
|
62
|
+
# @param zone [nil, String, TimeZone] The timezone to use to calculate
|
63
|
+
# the offset (defaults to system timezone, or ActiveSupport's Time.zone
|
64
|
+
# if set)
|
65
|
+
# @return [Rational] The offset in fraction of a day
|
66
|
+
def local_offset_for_datetime(dt, zone = nil)
|
67
|
+
dt = dt.new_offset(0)
|
68
|
+
time = ::Time.utc(dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec)
|
69
|
+
|
70
|
+
# Do we have ActiveSupport's TimeZone?
|
71
|
+
time = if time.respond_to?(:in_time_zone)
|
72
|
+
time.in_time_zone(zone || ::Time.zone)
|
73
|
+
else
|
74
|
+
time.localtime
|
75
|
+
end
|
76
|
+
|
77
|
+
Rational(time.utc_offset, 86400) # seconds in one day (24*60*60)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -14,6 +14,8 @@ module FmRest
|
|
14
14
|
TOKEN_STORE_INTERFACE = [:load, :store, :delete].freeze
|
15
15
|
LOGOUT_PATH_MATCHER = %r{\A(#{FmRest::V1::Connection::BASE_PATH}/[^/]+/sessions/)[^/]+\Z}.freeze
|
16
16
|
|
17
|
+
# @param app [#call]
|
18
|
+
# @param options [Hash]
|
17
19
|
def initialize(app, options = FmRest.default_connection_settings)
|
18
20
|
super(app)
|
19
21
|
@options = options
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fmrest/string_date"
|
4
|
+
|
5
|
+
module FmRest
|
6
|
+
module V1
|
7
|
+
class TypeCoercer < Faraday::Response::Middleware
|
8
|
+
# We use this date to represent a FileMaker time for consistency with
|
9
|
+
# ginjo-rfm
|
10
|
+
JULIAN_ZERO_DAY = "-4712/1/1"
|
11
|
+
|
12
|
+
COERCE_HYBRID = [:hybrid, "hybrid", true].freeze
|
13
|
+
COERCE_FULL = [:full, "full"].freeze
|
14
|
+
|
15
|
+
# @param app [#call]
|
16
|
+
# @param options [Hash]
|
17
|
+
def initialize(app, options = FmRest.default_connection_settings)
|
18
|
+
super(app)
|
19
|
+
@options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_complete(env)
|
23
|
+
return unless enabled?
|
24
|
+
return unless env.body.kind_of?(Hash)
|
25
|
+
|
26
|
+
data = env.body.dig("response", "data") || env.body.dig(:response, :data)
|
27
|
+
|
28
|
+
return unless data
|
29
|
+
|
30
|
+
data.each do |record|
|
31
|
+
field_data = record["fieldData"] || record[:fieldData]
|
32
|
+
portal_data = record["portalData"] || record[:portalData]
|
33
|
+
|
34
|
+
coerce_fields(field_data)
|
35
|
+
|
36
|
+
portal_data.try(:each_value) do |portal_records|
|
37
|
+
portal_records.each do |pr|
|
38
|
+
coerce_fields(pr)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def coerce_fields(hash)
|
47
|
+
hash.each do |k, v|
|
48
|
+
next unless v.is_a?(String)
|
49
|
+
next if k == "recordId" || k == :recordId || k == "modId" || k == :modId
|
50
|
+
|
51
|
+
if quick_check_timestamp(v)
|
52
|
+
begin
|
53
|
+
hash[k] = coerce_timestamp(v)
|
54
|
+
next
|
55
|
+
rescue ArgumentError
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
if quick_check_date(v)
|
60
|
+
begin
|
61
|
+
hash[k] = date_class.strptime(v, date_strptime_format)
|
62
|
+
next
|
63
|
+
rescue ArgumentError
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if quick_check_time(v)
|
68
|
+
begin
|
69
|
+
hash[k] = datetime_class.strptime("#{JULIAN_ZERO_DAY} #{v}", time_strptime_format)
|
70
|
+
next
|
71
|
+
rescue ArgumentError
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def coerce_timestamp(str)
|
78
|
+
str_timestamp = DateTime.strptime(str, datetime_strptime_format)
|
79
|
+
|
80
|
+
if local_timezone?
|
81
|
+
# Change the DateTime to the local timezone, keeping the same
|
82
|
+
# time and just modifying the timezone
|
83
|
+
offset = FmRest::V1.local_offset_for_datetime(str_timestamp)
|
84
|
+
str_timestamp = str_timestamp.new_offset(offset) - offset
|
85
|
+
end
|
86
|
+
|
87
|
+
if datetime_class == StringDateTime
|
88
|
+
str_timestamp = StringDateTime.new(str, str_timestamp)
|
89
|
+
end
|
90
|
+
|
91
|
+
str_timestamp
|
92
|
+
end
|
93
|
+
|
94
|
+
def date_class
|
95
|
+
@date_class ||=
|
96
|
+
case coerce_dates
|
97
|
+
when *COERCE_HYBRID
|
98
|
+
StringDate
|
99
|
+
when *COERCE_FULL
|
100
|
+
Date
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def datetime_class
|
105
|
+
@datetime_class ||=
|
106
|
+
case coerce_dates
|
107
|
+
when *COERCE_HYBRID
|
108
|
+
StringDateTime
|
109
|
+
when *COERCE_FULL
|
110
|
+
DateTime
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def date_fm_format
|
115
|
+
@options[:date_format] || DEFAULT_DATE_FORMAT
|
116
|
+
end
|
117
|
+
|
118
|
+
def timestamp_fm_format
|
119
|
+
@options[:timestamp_format] || DEFAULT_TIMESTAMP_FORMAT
|
120
|
+
end
|
121
|
+
|
122
|
+
def time_fm_format
|
123
|
+
@options[:time_format] || DEFAULT_TIME_FORMAT
|
124
|
+
end
|
125
|
+
|
126
|
+
def date_strptime_format
|
127
|
+
FmRest::V1.fm_date_to_strptime_format(date_fm_format)
|
128
|
+
end
|
129
|
+
|
130
|
+
def datetime_strptime_format
|
131
|
+
FmRest::V1.fm_date_to_strptime_format(timestamp_fm_format)
|
132
|
+
end
|
133
|
+
|
134
|
+
def time_strptime_format
|
135
|
+
@time_strptime_format ||=
|
136
|
+
"%Y/%m/%d " + FmRest::V1.fm_date_to_strptime_format(time_fm_format)
|
137
|
+
end
|
138
|
+
|
139
|
+
# We use a string length test, followed by regexp match test to try to
|
140
|
+
# identify date fields. Benchmarking shows this should be between 1 and 3
|
141
|
+
# orders of magnitude faster for fails (i.e. non-dates) than just using
|
142
|
+
# Date.strptime.
|
143
|
+
#
|
144
|
+
# user system total real
|
145
|
+
# strptime: 0.268496 0.000962 0.269458 ( 0.270865)
|
146
|
+
# re=~: 0.024872 0.000070 0.024942 ( 0.025057)
|
147
|
+
# re.match?: 0.019745 0.000095 0.019840 ( 0.020058)
|
148
|
+
# strptime fail: 0.141309 0.000354 0.141663 ( 0.142266)
|
149
|
+
# re=~ fail: 0.031637 0.000095 0.031732 ( 0.031872)
|
150
|
+
# re.match? fail: 0.011249 0.000056 0.011305 ( 0.011375)
|
151
|
+
# length fail: 0.007177 0.000024 0.007201 ( 0.007222)
|
152
|
+
#
|
153
|
+
# NOTE: The faster Regexp#match? was introduced in Ruby 2.4.0, so we
|
154
|
+
# can't really rely on it being available
|
155
|
+
if //.respond_to?(:match?)
|
156
|
+
def quick_check_timestamp(v)
|
157
|
+
v.length == timestamp_fm_format.length && FmRest::V1::fm_date_to_regexp(timestamp_fm_format).match?(v)
|
158
|
+
end
|
159
|
+
|
160
|
+
def quick_check_date(v)
|
161
|
+
v.length == date_fm_format.length && FmRest::V1::fm_date_to_regexp(date_fm_format).match?(v)
|
162
|
+
end
|
163
|
+
|
164
|
+
def quick_check_time(v)
|
165
|
+
v.length == time_fm_format.length && FmRest::V1::fm_date_to_regexp(time_fm_format).match?(v)
|
166
|
+
end
|
167
|
+
else
|
168
|
+
def quick_check_timestamp(v)
|
169
|
+
v.length == timestamp_fm_format.length && FmRest::V1::fm_date_to_regexp(timestamp_fm_format) =~ v
|
170
|
+
end
|
171
|
+
|
172
|
+
def quick_check_date(v)
|
173
|
+
v.length == date_fm_format.length && FmRest::V1::fm_date_to_regexp(date_fm_format) =~ v
|
174
|
+
end
|
175
|
+
|
176
|
+
def quick_check_time(v)
|
177
|
+
v.length == time_fm_format.length && FmRest::V1::fm_date_to_regexp(time_fm_format) =~ v
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def local_timezone?
|
182
|
+
@local_timezone ||= @options.fetch(:timezone, nil).try(:to_sym) == :local
|
183
|
+
end
|
184
|
+
|
185
|
+
def coerce_dates
|
186
|
+
@options.fetch(:coerce_dates, false)
|
187
|
+
end
|
188
|
+
|
189
|
+
alias_method :enabled?, :coerce_dates
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
data/lib/fmrest/v1/utils.rb
CHANGED
data/lib/fmrest/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fmrest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pedro Carbajal
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -231,7 +231,6 @@ files:
|
|
231
231
|
- ".travis.yml"
|
232
232
|
- ".yardopts"
|
233
233
|
- CHANGELOG.md
|
234
|
-
- CODE_OF_CONDUCT.md
|
235
234
|
- Gemfile
|
236
235
|
- LICENSE.txt
|
237
236
|
- README.md
|
@@ -242,20 +241,22 @@ files:
|
|
242
241
|
- lib/fmrest/spyke.rb
|
243
242
|
- lib/fmrest/spyke/base.rb
|
244
243
|
- lib/fmrest/spyke/container_field.rb
|
245
|
-
- lib/fmrest/spyke/json_parser.rb
|
246
244
|
- lib/fmrest/spyke/model.rb
|
247
245
|
- lib/fmrest/spyke/model/associations.rb
|
248
246
|
- lib/fmrest/spyke/model/attributes.rb
|
249
247
|
- lib/fmrest/spyke/model/auth.rb
|
250
248
|
- lib/fmrest/spyke/model/connection.rb
|
251
249
|
- lib/fmrest/spyke/model/container_fields.rb
|
250
|
+
- lib/fmrest/spyke/model/global_fields.rb
|
252
251
|
- lib/fmrest/spyke/model/http.rb
|
253
252
|
- lib/fmrest/spyke/model/orm.rb
|
254
253
|
- lib/fmrest/spyke/model/serialization.rb
|
255
254
|
- lib/fmrest/spyke/model/uri.rb
|
256
255
|
- lib/fmrest/spyke/portal.rb
|
257
256
|
- lib/fmrest/spyke/relation.rb
|
257
|
+
- lib/fmrest/spyke/spyke_formatter.rb
|
258
258
|
- lib/fmrest/spyke/validation_error.rb
|
259
|
+
- lib/fmrest/string_date.rb
|
259
260
|
- lib/fmrest/token_store.rb
|
260
261
|
- lib/fmrest/token_store/active_record.rb
|
261
262
|
- lib/fmrest/token_store/base.rb
|
@@ -265,11 +266,13 @@ files:
|
|
265
266
|
- lib/fmrest/v1.rb
|
266
267
|
- lib/fmrest/v1/connection.rb
|
267
268
|
- lib/fmrest/v1/container_fields.rb
|
269
|
+
- lib/fmrest/v1/dates.rb
|
268
270
|
- lib/fmrest/v1/paths.rb
|
269
271
|
- lib/fmrest/v1/raise_errors.rb
|
270
272
|
- lib/fmrest/v1/token_session.rb
|
271
273
|
- lib/fmrest/v1/token_store/active_record.rb
|
272
274
|
- lib/fmrest/v1/token_store/memory.rb
|
275
|
+
- lib/fmrest/v1/type_coercer.rb
|
273
276
|
- lib/fmrest/v1/utils.rb
|
274
277
|
- lib/fmrest/version.rb
|
275
278
|
homepage: https://github.com/beezwax/fmrest-ruby
|
data/CODE_OF_CONDUCT.md
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
# Contributor Covenant Code of Conduct
|
2
|
-
|
3
|
-
## Our Pledge
|
4
|
-
|
5
|
-
In the interest of fostering an open and welcoming environment, we as
|
6
|
-
contributors and maintainers pledge to making participation in our project and
|
7
|
-
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
-
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
-
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
-
orientation.
|
11
|
-
|
12
|
-
## Our Standards
|
13
|
-
|
14
|
-
Examples of behavior that contributes to creating a positive environment
|
15
|
-
include:
|
16
|
-
|
17
|
-
* Using welcoming and inclusive language
|
18
|
-
* Being respectful of differing viewpoints and experiences
|
19
|
-
* Gracefully accepting constructive criticism
|
20
|
-
* Focusing on what is best for the community
|
21
|
-
* Showing empathy towards other community members
|
22
|
-
|
23
|
-
Examples of unacceptable behavior by participants include:
|
24
|
-
|
25
|
-
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
-
advances
|
27
|
-
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
-
* Public or private harassment
|
29
|
-
* Publishing others' private information, such as a physical or electronic
|
30
|
-
address, without explicit permission
|
31
|
-
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
-
professional setting
|
33
|
-
|
34
|
-
## Our Responsibilities
|
35
|
-
|
36
|
-
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
-
behavior and are expected to take appropriate and fair corrective action in
|
38
|
-
response to any instances of unacceptable behavior.
|
39
|
-
|
40
|
-
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
-
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
-
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
-
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
-
threatening, offensive, or harmful.
|
45
|
-
|
46
|
-
## Scope
|
47
|
-
|
48
|
-
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
-
when an individual is representing the project or its community. Examples of
|
50
|
-
representing a project or community include using an official project e-mail
|
51
|
-
address, posting via an official social media account, or acting as an appointed
|
52
|
-
representative at an online or offline event. Representation of a project may be
|
53
|
-
further defined and clarified by project maintainers.
|
54
|
-
|
55
|
-
## Enforcement
|
56
|
-
|
57
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
-
reported by contacting the project team at pedro_c@beezwax.net. All
|
59
|
-
complaints will be reviewed and investigated and will result in a response that
|
60
|
-
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
-
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
-
Further details of specific enforcement policies may be posted separately.
|
63
|
-
|
64
|
-
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
-
faith may face temporary or permanent repercussions as determined by other
|
66
|
-
members of the project's leadership.
|
67
|
-
|
68
|
-
## Attribution
|
69
|
-
|
70
|
-
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
-
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
-
|
73
|
-
[homepage]: http://contributor-covenant.org
|
74
|
-
[version]: http://contributor-covenant.org/version/1/4/
|