metaractor 3.0.1 → 3.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8b28e07d981c3224d3e209a34961e685de8859e7bec198c8f64ac08456219e7
4
- data.tar.gz: d2ea3446b24661a347bb38087df37b6208ce62a742f7a7edfd2332a7137e1acd
3
+ metadata.gz: 3a84ca8402879e172bd7dfb5cd494b41de2639e85080b3052c503a68d4406234
4
+ data.tar.gz: 6db8d581e34cb71fcbf27bcb590978d31301b7ad232fc478e31d7d9fae213eff
5
5
  SHA512:
6
- metadata.gz: 61e4852078088812a0a53b33601d555aa8a2bdd3b5d50f56bd96c8c50729f45aa7a788eb305f82bb747f973bfde421c2904385c35a4428687bd7f9621ab382e3
7
- data.tar.gz: 2cd047d8213c8c4197395545ab4f1b448aea33c6985f0ec0dc6901a66d923195be2ac510e0a73ee414643200d22b5d5b8c88da90ce53cb22d7cb9f35b825beaa
6
+ metadata.gz: 364eb4ae9ddcf5a836ea876ecece48013c16b20b4a82a354c4e277d954df3cb828ba8252d48b3302dbd597f908f6c52a73909124cb3101a2e6a6b69e2f97afc6
7
+ data.tar.gz: dd45bf611c8edfafadc6fce753f682f429803082157a66f5c9c92e3a2daa7981d98483a65fd88bd73a5bf55c8cfd83edc92692e48e58186631fdf2a38397bf67
@@ -0,0 +1,93 @@
1
+ env:
2
+ BUILDKITE_PLUGIN_DOCKER_CACHE_S3_BUCKET: "outstand-buildkite-cache"
3
+ # BUILDKITE_PLUGIN_DOCKER_CACHE_VOLUME_DEBUG: "true"
4
+ BUILDKITE_PLUGIN_DOCKER_COMPOSE_SHELL: "false"
5
+ # BUILDKITE_PLUGIN_DOCKER_COMPOSE_UPLOAD_CONTAINER_LOGS: "always"
6
+ BUILDKITE_PLUGIN_DOCKER_COMPOSE_PULL_RETRIES: 5
7
+ BUILDKITE_PLUGIN_DOCKER_COMPOSE_PUSH_RETRIES: 5
8
+ PLUGIN_DOCKER_COMPOSE_VERSION: "03d746fbf5171b217b732ff7d8a3e417d664df1c"
9
+ PLUGIN_DOCKER_CACHE_VERSION: "50ecc80f736a4a3a0ab1f5990e58ae8e536c85e1"
10
+ WORKSPACE_DIR: "${BUILDKITE_BUILD_CHECKOUT_PATH}"
11
+
12
+ steps:
13
+ - label: ":docker: Build"
14
+ key: build
15
+ plugins:
16
+ - seek-oss/aws-sm#v2.3.1:
17
+ env:
18
+ DOCKER_LOGIN_PASSWORD: "/buildkite/docker_password"
19
+
20
+ - docker-login#v2.1.0:
21
+ username: outstandci
22
+ retries: 2
23
+
24
+ - ecr#v2.5.0:
25
+ login: true
26
+ region: "us-east-1"
27
+
28
+ - https://github.com/outstand/docker-compose-buildkite-plugin.git#${PLUGIN_DOCKER_COMPOSE_VERSION}:
29
+ build: metaractor
30
+ image-repository: 786715713882.dkr.ecr.us-east-1.amazonaws.com/ci-images
31
+ config:
32
+ - compose.yml
33
+
34
+ - label: ":bundler: :rubygems:"
35
+ key: bundle_install
36
+ command: bundle install
37
+ depends_on: build
38
+ plugins:
39
+ - seek-oss/aws-sm#v2.3.1:
40
+ env:
41
+ DOCKER_LOGIN_PASSWORD: "/buildkite/docker_password"
42
+
43
+ - docker-login#v2.1.0:
44
+ username: outstandci
45
+ retries: 2
46
+
47
+ - ecr#v2.5.0:
48
+ login: true
49
+ region: "us-east-1"
50
+
51
+ - https://github.com/outstand/docker-compose-buildkite-plugin.git#${PLUGIN_DOCKER_COMPOSE_VERSION}:
52
+ run: metaractor
53
+ dependencies: false
54
+ config:
55
+ - compose.yml
56
+
57
+ - https://github.com/outstand/docker-cache-buildkite-plugin.git#${PLUGIN_DOCKER_CACHE_VERSION}:
58
+ name: bundler-cache
59
+ keys:
60
+ - v1-bundler-cache-{{ arch }}-{{ checksum "metaractor.gemspec" }}-{{ checksum "Gemfile" }}
61
+ - v1-bundler-cache-{{ arch }}-
62
+ save: true
63
+ volumes:
64
+ - bundler-data
65
+
66
+ - label: ":ruby: Specs"
67
+ command: rspec spec
68
+ depends_on: bundle_install
69
+ plugins:
70
+ - seek-oss/aws-sm#v2.3.1:
71
+ env:
72
+ DOCKER_LOGIN_PASSWORD: "/buildkite/docker_password"
73
+
74
+ - docker-login#v2.1.0:
75
+ username: outstandci
76
+ retries: 2
77
+
78
+ - ecr#v2.5.0:
79
+ login: true
80
+ region: "us-east-1"
81
+
82
+ - https://github.com/outstand/docker-compose-buildkite-plugin.git#${PLUGIN_DOCKER_COMPOSE_VERSION}:
83
+ run: metaractor
84
+ config:
85
+ - compose.yml
86
+
87
+ - https://github.com/outstand/docker-cache-buildkite-plugin.git#${PLUGIN_DOCKER_CACHE_VERSION}:
88
+ name: bundler-cache
89
+ keys:
90
+ - v1-bundler-cache-{{ arch }}-{{ checksum "metaractor.gemspec" }}-{{ checksum "Gemfile" }}
91
+ - v1-bundler-cache-{{ arch }}-
92
+ volumes:
93
+ - bundler-data
data/.rspec CHANGED
@@ -1,2 +1,4 @@
1
1
  --color
2
2
  --require spec_helper
3
+ --format progress
4
+ --format RSpec::Buildkite::AnnotationFormatter
data/Deskfile ADDED
@@ -0,0 +1,3 @@
1
+ rspec() {
2
+ docker compose run --rm metaractor rspec "$@"
3
+ }
data/Dockerfile CHANGED
@@ -1,30 +1,78 @@
1
- FROM ruby:2.6.5-alpine
1
+ FROM outstand/su-exec:latest as su-exec
2
+ FROM outstand/fixuid as fixuid
3
+
4
+ FROM ruby:2.7.6-bullseye
2
5
  LABEL maintainer="Ryan Schlesinger <ryan@outstand.com>"
3
6
 
4
- RUN addgroup -g 1000 -S metaractor && \
5
- adduser -u 1000 -S -s /bin/ash -G metaractor metaractor && \
6
- apk add --no-cache \
7
- ca-certificates \
8
- tini \
9
- su-exec \
10
- build-base \
11
- git \
12
- openssh
7
+ SHELL ["/bin/bash", "-o", "pipefail", "-c"]
8
+ ENV DEBIAN_FRONTEND=noninteractive
9
+
10
+ RUN set -eux; \
11
+ \
12
+ groupadd -g 1000 metaractor && \
13
+ useradd -u 1000 -g metaractor -ms /bin/bash metaractor && \
14
+ \
15
+ apt-get update -y; \
16
+ apt-get install -y \
17
+ ca-certificates \
18
+ curl \
19
+ git \
20
+ build-essential \
21
+ tini \
22
+ ; \
23
+ apt-get clean; \
24
+ rm -f /var/lib/apt/lists/*_*
25
+
26
+ # install su-exec
27
+ COPY --from=su-exec /sbin/su-exec /sbin/su-exec
13
28
 
14
- ENV BUNDLER_VERSION 2.1.4
15
- RUN gem install bundler -v ${BUNDLER_VERSION} -i /usr/local/lib/ruby/gems/$(ls /usr/local/lib/ruby/gems) --force
29
+ # install fixuid
30
+ COPY --from=fixuid /usr/local/bin/fixuid /usr/local/bin/fixuid
31
+ RUN set -eux; \
32
+ \
33
+ chmod 4755 /usr/local/bin/fixuid; \
34
+ USER=metaractor; \
35
+ GROUP=metaractor; \
36
+ mkdir -p /etc/fixuid; \
37
+ printf "user: $USER\ngroup: $GROUP\n" > /etc/fixuid/config.yml
38
+
39
+ ENV BUNDLER_VERSION 2.3.20
40
+ ENV GITHUB_CLI_VERSION 2.14.4
41
+ ENV GITHUB_CLI_CHECKSUM b0073fdcc07d1de5a19a1a782c7ad9f593f991da06a809ea39f0b6148869aa96
42
+ RUN set -eux; \
43
+ \
44
+ mkdir -p /tmp/build; \
45
+ cd /tmp/build; \
46
+ \
47
+ gem install bundler -v ${BUNDLER_VERSION} -i /usr/local/lib/ruby/gems/$(ls /usr/local/lib/ruby/gems) --force; \
48
+ curl -fsSL https://github.com/cli/cli/releases/download/v${GITHUB_CLI_VERSION}/gh_${GITHUB_CLI_VERSION}_linux_amd64.deb -o gh_${GITHUB_CLI_VERSION}_linux_amd64.deb; \
49
+ echo "${GITHUB_CLI_CHECKSUM} gh_${GITHUB_CLI_VERSION}_linux_amd64.deb" | sha256sum --check; \
50
+ apt-get update -y; \
51
+ apt-get install -y --no-install-recommends \
52
+ ./gh_${GITHUB_CLI_VERSION}_linux_amd64.deb \
53
+ ; \
54
+ apt-get clean; \
55
+ rm -f /var/lib/apt/lists/*_*; \
56
+ rm -rf /tmp/build
57
+
58
+ COPY brew-shim /usr/bin/brew
16
59
 
17
60
  WORKDIR /metaractor
18
- RUN chown -R metaractor:metaractor /metaractor
61
+ RUN set -eux; \
62
+ \
63
+ chown -R metaractor:metaractor /metaractor
64
+
19
65
  USER metaractor
20
66
 
21
67
  COPY --chown=metaractor:metaractor Gemfile metaractor.gemspec /metaractor/
22
68
  COPY --chown=metaractor:metaractor lib/metaractor/version.rb /metaractor/lib/metaractor/
23
- RUN git config --global push.default simple
69
+ RUN set -eux; \
70
+ \
71
+ git config --global push.default simple
24
72
  COPY --chown=metaractor:metaractor . /metaractor/
25
73
 
26
74
  USER root
27
75
  COPY docker-entrypoint.sh /docker-entrypoint.sh
28
76
 
29
- ENTRYPOINT ["/sbin/tini", "-g", "--", "/docker-entrypoint.sh"]
77
+ ENTRYPOINT ["/usr/bin/tini", "-g", "--", "/docker-entrypoint.sh"]
30
78
  CMD ["rspec", "spec"]
data/Gemfile CHANGED
@@ -1,5 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
- gem 'pry-byebug'
5
- gem 'awesome_print'
4
+ gem 'rspec-buildkite', github: 'outstand/rspec-buildkite', branch: 'error-output'
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Metaractor
1
+ # Metaractor [![Build status](https://badge.buildkite.com/70063a5154eb7366b8b7fd65a875c5f64301bc60f6d29a2ad7.svg)](https://buildkite.com/outstand/metaractor)
2
2
  Adds parameter validation and error control to [interactor](https://github.com/collectiveidea/interactor).
3
3
 
4
4
  ## Installation
@@ -103,6 +103,24 @@ This works with `allow_blank` and can also be anything that responds to `#call`.
103
103
  parameter :role, allow_blank: true, default: -> { context.default_role }
104
104
  ```
105
105
 
106
+ #### Typecasting/Coersion
107
+ You can supply Metaractor with a callable that will typecast incoming parameters:
108
+ ```ruby
109
+ optional :needs_to_be_a_string, type: ->(value) { value.to_s }
110
+ ```
111
+
112
+ You can also configure Metaractor with named types and use them:
113
+ ```ruby
114
+ Metaractor.configure do |config|
115
+ config.register_type(:boolean, ->(value) { ActiveModel::Type::Boolean.new.cast(value) })
116
+ end
117
+ ```
118
+ ```ruby
119
+ required :is_awesome, type: :boolean
120
+ ```
121
+
122
+ **Note**: Typecasters will _not_ be called on `nil` values.
123
+
106
124
  ### Custom Validation
107
125
  Metaractor supports doing custom validation before any user supplied before_hooks run.
108
126
  ```ruby
data/brew-shim ADDED
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+
3
+ set -euo pipefail
4
+
5
+ # Docker host has the following git config:
6
+ # helper = !$(brew --prefix)/bin/gh auth git-credential
7
+ #
8
+ # We're going to lie inside the container so we can find the local gh
9
+
10
+ echo "/usr"
@@ -1,24 +1,35 @@
1
- version: '3.6'
2
1
  services:
3
2
  metaractor:
4
3
  build: .
5
4
  image: outstand/metaractor:dev
6
5
  stdin_open: true
7
6
  tty: true
7
+ environment:
8
+ FIXUID:
9
+ FIXGID:
10
+ BUILDKITE:
11
+ BUILDKITE_BUILD_URL:
12
+ BUILDKITE_JOB_ID:
13
+ BUILDKITE_AGENT_ACCESS_TOKEN:
8
14
  volumes:
9
15
  - bundler-data:/usr/local/bundle
10
16
  - .:/metaractor
17
+
11
18
  release:
12
19
  image: outstand/metaractor:dev
13
20
  stdin_open: true
14
21
  tty: true
15
22
  command: rake release
23
+ environment:
24
+ FIXUID:
25
+ FIXGID:
16
26
  volumes:
17
27
  - bundler-data:/usr/local/bundle
18
28
  - ~/.gitconfig:/home/metaractor/.gitconfig
19
29
  - ~/.gitconfig.user:/home/metaractor/.gitconfig.user
20
- - ~/.ssh/id_rsa:/home/metaractor/.ssh/id_rsa
21
- - ~/.gem:/home/metaractor/.gem
30
+ - ~/.config/gh/hosts.yml:/home/metaractor/.config/gh/hosts.yml
31
+ - ~/.local/share/gem/credentials:/home/metaractor/.local/share/gem/credentials
32
+ - ~/.local/share/gem/credentials:/home/metaractor/.gem/credentials
22
33
 
23
34
  volumes:
24
35
  bundler-data:
data/docker-entrypoint.sh CHANGED
@@ -1,5 +1,20 @@
1
- #!/bin/sh
2
- set -e
1
+ #!/bin/bash
2
+
3
+ set -euo pipefail
4
+
5
+ su-exec ${FIXUID:?Missing FIXUID var}:${FIXGID:?Missing FIXGID var} fixuid
6
+
7
+ chown_dir() {
8
+ dir=$1
9
+ if [[ -d ${dir} ]] && [[ "$(stat -c %u:%g ${dir})" != "${FIXUID}:${FIXGID}" ]]; then
10
+ echo chown $dir
11
+ chown metaractor:metaractor $dir
12
+ fi
13
+ }
14
+
15
+ chown_dir /usr/local/bundle
16
+ chown_dir /home/metaractor/.local/share/gem
17
+ chown_dir /home/metaractor/.gem
3
18
 
4
19
  if [ "$(which "$1")" = '' ]; then
5
20
  if [ "$(ls -A /usr/local/bundle/bin)" = '' ]; then
@@ -14,7 +29,7 @@ if [ "$1" = 'bundle' ]; then
14
29
  elif ls /usr/local/bundle/bin | grep -q "\b$1\b"; then
15
30
  set -- su-exec metaractor bundle exec "$@"
16
31
 
17
- su-exec metaractor ash -c 'bundle check || bundle install'
32
+ su-exec metaractor bash -c 'bundle check || bundle install'
18
33
  fi
19
34
 
20
35
  exec "$@"
@@ -71,18 +71,19 @@ module Metaractor
71
71
 
72
72
  def add(error: {}, errors: {}, object: nil)
73
73
  trees = []
74
- [error, errors].each do |h|
74
+ [error, errors].each do |h|
75
75
  tree = nil
76
76
  if h.is_a? Metaractor::Errors
77
77
  tree = Sycamore::Tree.from(h.instance_variable_get(:@tree))
78
78
  else
79
- tree = Sycamore::Tree.from(h)
79
+ tree = Sycamore::Tree.from(normalize_error_hash(h))
80
80
  end
81
81
 
82
82
  unless tree.empty?
83
- if tree.nodes.any? {|node| tree.strict_leaf?(node) }
83
+ if tree.nodes.any? { |node| tree.strict_leaf?(node) }
84
84
  raise ArgumentError, "Invalid hash!"
85
85
  end
86
+
86
87
  trees << tree
87
88
  end
88
89
  end
@@ -220,5 +221,34 @@ module Metaractor
220
221
  end
221
222
  end
222
223
 
224
+ def normalize_error_hash(hash)
225
+ deep_transform_values_in_object(hash, &method(:transform_delegator))
226
+ end
227
+
228
+ def transform_delegator(value)
229
+ if value.is_a?(Delegator)
230
+ if value.respond_to?(:to_hash)
231
+ deep_transform_values_in_object(value.to_hash, &method(:transform_delegator))
232
+ elsif value.respond_to?(:to_a)
233
+ deep_transform_values_in_object(value.to_a, &method(:transform_delegator))
234
+ else
235
+ value
236
+ end
237
+ else
238
+ value
239
+ end
240
+ end
241
+
242
+ # Lifted from Rails
243
+ def deep_transform_values_in_object(object, &block)
244
+ case object
245
+ when Hash
246
+ object.transform_values { |value| deep_transform_values_in_object(value, &block) }
247
+ when Array
248
+ object.map { |e| deep_transform_values_in_object(e, &block) }
249
+ else
250
+ yield(object)
251
+ end
252
+ end
223
253
  end
224
254
  end
@@ -14,6 +14,7 @@ module Metaractor
14
14
  before :remove_blank_values
15
15
  before :apply_defaults
16
16
  before :validate_required_parameters
17
+ before :apply_types
17
18
  end
18
19
  end
19
20
 
@@ -37,6 +38,10 @@ module Metaractor
37
38
  @options[key]
38
39
  end
39
40
 
41
+ def has_key?(key)
42
+ @options.has_key?(key)
43
+ end
44
+
40
45
  def dig(name, *names)
41
46
  @options.dig(name, *names)
42
47
  end
@@ -142,7 +147,7 @@ module Metaractor
142
147
 
143
148
  def apply_defaults
144
149
  parameters.each do |name, parameter|
145
- next unless parameter[:default]
150
+ next unless parameter.has_key?(:default)
146
151
 
147
152
  unless context.has_key?(name)
148
153
  context[name] = _parameter_default(name)
@@ -160,6 +165,23 @@ module Metaractor
160
165
  end
161
166
  end
162
167
 
168
+ def apply_types
169
+ parameters.each do |name, parameter|
170
+ next unless parameter[:type]
171
+
172
+ if context.has_key?(name) && context[name] != nil
173
+ callable = parameter[:type]
174
+
175
+ if callable.is_a?(Symbol)
176
+ callable = Metaractor.types[callable]
177
+ raise ArgumentError, "No such type: #{parameter[:type]}" if callable.nil?
178
+ end
179
+
180
+ context[name] = callable.call(context[name])
181
+ end
182
+ end
183
+ end
184
+
163
185
  def validate_required_parameters
164
186
  context.errors ||= []
165
187
 
@@ -1,3 +1,3 @@
1
1
  module Metaractor
2
- VERSION = "3.0.1"
2
+ VERSION = "3.2.0"
3
3
  end
data/lib/metaractor.rb CHANGED
@@ -77,4 +77,16 @@ module Metaractor
77
77
  def self.hash_formatter=(callable)
78
78
  @hash_formatter = callable
79
79
  end
80
+
81
+ def self.types
82
+ @types ||= {}
83
+ end
84
+
85
+ def self.register_type(type, callable)
86
+ types[type] = callable
87
+ end
88
+
89
+ def self.clear_types!
90
+ @types = {}
91
+ end
80
92
  end
data/metaractor.gemspec CHANGED
@@ -11,7 +11,10 @@ Gem::Specification.new do |spec|
11
11
  spec.email = ['ryan@outstand.com']
12
12
 
13
13
  spec.summary = %q{Adds parameter validation and error control to interactor}
14
- spec.homepage = 'https://github.com/outstand/metaractor'
14
+ spec.metadata = {
15
+ "homepage_uri" => "https://github.com/outstand/metaractor",
16
+ "source_code_uri" => "https://github.com/outstand/metaractor"
17
+ }
15
18
 
16
19
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
20
  spec.bindir = 'exe'
@@ -27,4 +30,5 @@ Gem::Specification.new do |spec|
27
30
  spec.add_development_dependency 'rspec', '~> 3.9'
28
31
  spec.add_development_dependency 'awesome_print', '~> 1.8'
29
32
  spec.add_development_dependency 'pry-byebug', '~> 3.9'
33
+ spec.add_development_dependency 'activemodel', '~> 6.1'
30
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metaractor
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Schlesinger
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-21 00:00:00.000000000 Z
11
+ date: 2022-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: interactor
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '3.9'
125
+ - !ruby/object:Gem::Dependency
126
+ name: activemodel
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '6.1'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '6.1'
125
139
  description:
126
140
  email:
127
141
  - ryan@outstand.com
@@ -129,14 +143,17 @@ executables: []
129
143
  extensions: []
130
144
  extra_rdoc_files: []
131
145
  files:
146
+ - ".buildkite/pipeline.yml"
132
147
  - ".gitignore"
133
148
  - ".rspec"
149
+ - Deskfile
134
150
  - Dockerfile
135
151
  - Gemfile
136
152
  - LICENSE
137
153
  - README.md
138
154
  - Rakefile
139
- - docker-compose.yml
155
+ - brew-shim
156
+ - compose.yml
140
157
  - docker-entrypoint.sh
141
158
  - lib/metaractor.rb
142
159
  - lib/metaractor/chain_failures.rb
@@ -153,10 +170,12 @@ files:
153
170
  - lib/metaractor/spec.rb
154
171
  - lib/metaractor/version.rb
155
172
  - metaractor.gemspec
156
- homepage: https://github.com/outstand/metaractor
173
+ homepage:
157
174
  licenses:
158
175
  - Apache-2.0
159
- metadata: {}
176
+ metadata:
177
+ homepage_uri: https://github.com/outstand/metaractor
178
+ source_code_uri: https://github.com/outstand/metaractor
160
179
  post_install_message:
161
180
  rdoc_options: []
162
181
  require_paths:
@@ -172,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
191
  - !ruby/object:Gem::Version
173
192
  version: '0'
174
193
  requirements: []
175
- rubygems_version: 3.0.3
194
+ rubygems_version: 3.1.6
176
195
  signing_key:
177
196
  specification_version: 4
178
197
  summary: Adds parameter validation and error control to interactor