appydays 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f235dd9ffc485d78888e9cc325dc090dba1f40b8cf4a2cb9e24df2074149520
4
- data.tar.gz: a34bed5f98cf79bad2401c94b254a41deb28f7165fb86e5378ec96bec8913b9d
3
+ metadata.gz: fa853f6dccbb995b590e7a13204f7e02f7c8bb992630fa19344a15e751149bcf
4
+ data.tar.gz: 235896b58a7e3a1fac6fa742cf01dfd8d129a54182d09ae1dfb65ad31b60448c
5
5
  SHA512:
6
- metadata.gz: 4e62429beede3533e76c7f245ca3b653c5b702f5fbd64571f7381a49c6975043e8d1c8a37f60a47c3aed7422ec83a8408da66852a47ca3c28840852b2e3feac0
7
- data.tar.gz: edb5ebdb0c6b073e8b6c8d4fdbd14d548592fbea901a6e6c0cfc7ef785231158ce94ff8bbbd5496637414c17239e47dd633e65db325c5316e8889d95d07fd564
6
+ metadata.gz: 931e7d037f44b697864b519f3efe6577a02f9923e814c2201f4c1ab1aea9430416f3cfc6dd63b26f116916b32ba08d608b03919cb7be52f173b11514e73d860a
7
+ data.tar.gz: 161d5c8a250623e558426ee6be2dae1135717c6599de90d4ff38e2276f2d5b2b65de729eaf16443ae18e92f6bb923771762d9c92ab8c2bec33731c7ed154645d
@@ -86,6 +86,8 @@ module Appydays::Configurable
86
86
  # If convert is passed, that is used as the converter so the default value can be any type.
87
87
  # key: The key to lookup the config value from the environment.
88
88
  # If nil, use an auto-generated combo of the configuration key and method name.
89
+ # If key is an array, look up each (string) value as a key.
90
+ # The first non-nil (ie, `ENV.fetch(x, nil)`) value will be used.
89
91
  # convert: If provided, call it with the string value so it can be parsed.
90
92
  # For example, you can parse a string JSON value here.
91
93
  # Convert will not be called with the default value.
@@ -106,7 +108,8 @@ module Appydays::Configurable
106
108
  end
107
109
 
108
110
  key ||= "#{@group_key}_#{name}".upcase
109
- env_value = ENV.fetch(key, nil)
111
+ keys = Array(key)
112
+ env_value = keys.filter_map { |k| ENV.fetch(k, nil) }.first
110
113
  converter = self._converter(default, convert)
111
114
  value = env_value.nil? ? default : converter[env_value]
112
115
  value = installer._set_value(name, value, side_effect)
@@ -5,8 +5,9 @@ require "sequel/database/logging"
5
5
 
6
6
  class Sequel::Database
7
7
  def log_exception(exception, message)
8
+ level = message.match?(/^SELECT NULL AS "?nil"? FROM .* LIMIT 1$/i) ? :debug : :error
8
9
  log_each(
9
- :error,
10
+ level,
10
11
  proc { "#{exception.class}: #{exception.message.strip if exception.message}: #{message}" },
11
12
  proc { ["sequel_exception", {sequel_message: message}, exception] },
12
13
  )
@@ -19,12 +19,15 @@ module Appydays::SpecHelpers
19
19
  #
20
20
 
21
21
  class HaveALineMatching
22
+ include RSpec::Matchers::Composable
23
+
22
24
  def initialize(regexp)
23
25
  @regexp = regexp
24
26
  end
25
27
 
26
28
  def matches?(target)
27
29
  @target = target
30
+ @target = @target.lines if @target.is_a?(String)
28
31
  return @target.find do |obj|
29
32
  obj.to_s.match(@regexp)
30
33
  end
@@ -43,9 +46,9 @@ module Appydays::SpecHelpers
43
46
  alias failure_message_for_should_not failure_message_when_negated
44
47
  end
45
48
 
46
- ### RSpec matcher -- set up the expectation that the lefthand side
47
- ### is Enumerable, and that at least one of the objects yielded
48
- ### while iterating matches +regexp+ when converted to a String.
49
+ # RSpec matcher -- set up the expectation that the lefthand side
50
+ # is Enumerable, and that at least one of the objects yielded
51
+ # while iterating matches +regexp+ when converted to a String.
49
52
  module_function def have_a_line_matching(regexp)
50
53
  return HaveALineMatching.new(regexp)
51
54
  end
@@ -54,39 +57,73 @@ module Appydays::SpecHelpers
54
57
  return RSpec::Matchers::BuiltIn::HaveAttributes.new(length: x)
55
58
  end
56
59
 
57
- # Matcher that will compare a string or time expected against a string or time actual,
58
- # within a tolerance (default to 1 millisecond).
59
- #
60
- # expect(last_response).to have_json_body.that_includes(
61
- # closes_at: match_time('2025-12-01T00:00:00.000+00:00').within(1.second))
62
- #
63
- RSpec::Matchers.define(:match_time) do |expected|
64
- match do |actual|
65
- @tolerance ||= 0.001
66
- RSpec::Matchers::BuiltIn::BeWithin.new(@tolerance).of(self.time(expected)).matches?(self.time(actual))
60
+ class MatchTime
61
+ include RSpec::Matchers::Composable
62
+
63
+ def initialize(expected)
64
+ @expected = expected
65
+ if expected == :now
66
+ @expected_t = Time.now
67
+ @tolerance = 5
68
+ else
69
+ @expected_t = self.time(expected)
70
+ @tolerance = 0.001
71
+ end
67
72
  end
68
73
 
69
- failure_message do |actual|
70
- "expected ids %s to be within %s of %s" % [self.time(actual), @tolerance, self.time(expected)]
74
+ def time(s)
75
+ return nil if s.nil?
76
+ return Time.parse(s) if s.is_a?(String)
77
+ return Time.at(s) if s.is_a?(Numeric)
78
+ return s.to_time
79
+ end
80
+
81
+ def matches?(actual)
82
+ @actual_t = self.time(actual)
83
+ @actual_t = self.change_tz(@actual_t, @expected_t.zone) if @actual_t
84
+ return RSpec::Matchers::BuiltIn::BeWithin.new(@tolerance).of(@expected_t).matches?(@actual_t)
85
+ end
86
+
87
+ protected def change_tz(t, zone)
88
+ return t.change(zone: zone) if t.respond_to?(:change)
89
+ prev_tz = ENV.fetch("TZ", nil)
90
+ begin
91
+ ENV["TZ"] = zone
92
+ return Time.at(t.to_f)
93
+ ensure
94
+ ENV["TZ"] = prev_tz
95
+ end
71
96
  end
72
97
 
73
- chain :within do |tolerance|
98
+ def within(tolerance)
74
99
  @tolerance = tolerance
100
+ return self
75
101
  end
76
102
 
77
- def time(s)
78
- return Time.parse(s) if s.is_a?(String)
79
- return s.to_time
103
+ def failure_message
104
+ return "expected %s to be within %s of %s" % [@actual_t, @tolerance, @expected_t]
80
105
  end
81
106
  end
82
107
 
108
+ # Matcher that will compare a string or time expected against a string or time actual,
109
+ # within a tolerance (default to 1 millisecond).
110
+ #
111
+ # Use match_time(:now) to automatically `match_time(Time.now).within(5.seconds)`.
112
+ #
113
+ # expect(last_response).to have_json_body.that_includes(
114
+ # closes_at: match_time('2025-12-01T00:00:00.000+00:00').within(1.second))
115
+ #
116
+ def match_time(expected)
117
+ return MatchTime.new(expected)
118
+ end
119
+
83
120
  # Matcher that will compare a string or Money expected against a string or Money actual.
84
121
  #
85
122
  # expect(order.total).to cost('$25')
86
123
  #
87
- RSpec::Matchers.define(:cost) do |expected|
124
+ RSpec::Matchers.define(:cost) do |expected, currency|
88
125
  match do |actual|
89
- @base = RSpec::Matchers::BuiltIn::Eq.new(self.money(expected))
126
+ @base = RSpec::Matchers::BuiltIn::Eq.new(self.money(expected, currency))
90
127
  @base.matches?(self.money(actual))
91
128
  end
92
129
 
@@ -94,12 +131,37 @@ module Appydays::SpecHelpers
94
131
  @base.failure_message
95
132
  end
96
133
 
97
- def money(s)
98
- return Monetize.parse(s) if s.is_a?(String)
134
+ def money(s, currency=nil)
135
+ if (m = self.tryparse(s, currency))
136
+ return m
137
+ end
99
138
  return s if s.is_a?(Money)
100
- return Money.new(s) if s.is_a?(Integer)
139
+ return Money.new(s, currency) if s.is_a?(Integer)
101
140
  return Money.new(s[:cents], s[:currency]) if s.respond_to?(:key?) && s.key?(:cents) && s.key?(:currency)
102
141
  raise "#{s} type #{s.class.name} not convertable to Money (add support or use supported type)"
103
142
  end
143
+
144
+ def tryparse(s, currency)
145
+ # We need to capture some global settings while we parse, and set them back after.
146
+ orig_default = Money.instance_variable_get(:@default_currency)
147
+ orig_assume = Monetize.assume_from_symbol
148
+ return nil unless s.is_a?(String)
149
+ # See https://github.com/RubyMoney/monetize/issues/161
150
+ # To get around this, need to use a valid custom currency
151
+ begin
152
+ Money::Currency.new("APPYDAYS")
153
+ rescue Money::Currency::UnknownCurrency
154
+ Money::Currency.register(iso_code: "APPYDAYS", subunit_to_unit: 1)
155
+ end
156
+ Money.default_currency = currency || orig_default || "APPYDAYS"
157
+ Monetize.assume_from_symbol = true
158
+ m = Monetize.parse!(s)
159
+ return m unless m.currency == "APPYDAYS"
160
+ raise Money::Currency::UnknownCurrency,
161
+ "Could not parse currency from '#{s}'. It needs a symbol, or set default_currency."
162
+ ensure
163
+ Money.default_currency = orig_default
164
+ Monetize.assume_from_symbol = orig_assume
165
+ end
104
166
  end
105
167
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appydays
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appydays
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lithic Tech
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-29 00:00:00.000000000 Z
11
+ date: 2022-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '4.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: monetize
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: money
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '6.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '6.0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: rack
43
71
  requirement: !ruby/object:Gem::Requirement