simple-service 0.1.1
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 +7 -0
- data/.gitignore +12 -0
- data/.rubocop.yml +96 -0
- data/.tm_properties +1 -0
- data/Gemfile +14 -0
- data/Makefile +4 -0
- data/README.md +1 -0
- data/Rakefile +6 -0
- data/VERSION +1 -0
- data/bin/bundle +105 -0
- data/bin/console +15 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/lib/simple/service/action/comment.rb +57 -0
- data/lib/simple/service/action/method_reflection.rb +70 -0
- data/lib/simple/service/action/parameter.rb +46 -0
- data/lib/simple/service/action.rb +193 -0
- data/lib/simple/service/context.rb +52 -0
- data/lib/simple/service/version.rb +24 -0
- data/lib/simple/service.rb +89 -0
- data/lib/simple-service.rb +3 -0
- data/lib/simple.rb +2 -0
- data/log/.gitkeep +0 -0
- data/scripts/release +2 -0
- data/scripts/release.rb +91 -0
- data/scripts/stats +4 -0
- data/scripts/watch +2 -0
- data/simple-service.gemspec +25 -0
- data/spec/simple/service/context_spec.rb +37 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/004_simplecov.rb +13 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9641586855a538c0b450d4731d39d28f23c4ee19abec379f5ac9047888fe9106
|
4
|
+
data.tar.gz: a395f0a7815935557163cc81532ef7e3fc2b97dfc2bf7347e32460481589dd1d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 43a8d60d9c7b1f14913f431b24ebd50db6a41c2b951a41bd8b1c136d466b2d198b0582e7adcbc34b0df0c81206abe79fda05ea25f8e9c74c4f52a5722e23146a
|
7
|
+
data.tar.gz: a0376b41661076f53a4ffe6a20650efc6dda513f762ddd0e96d7c7da0f568b6dc2e687c4fbfb67da759f7ac076ce115468a0fd2ba63a686f7ab15504a1ee5cd8
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.3
|
3
|
+
Exclude:
|
4
|
+
- 'spec/**/*'
|
5
|
+
- 'test/**/*'
|
6
|
+
- 'bin/**/*'
|
7
|
+
- 'tasks/release.rake'
|
8
|
+
- '*.gemspec'
|
9
|
+
- 'Gemfile'
|
10
|
+
- 'Rakefile'
|
11
|
+
- 'scripts/*.rb'
|
12
|
+
|
13
|
+
Metrics/LineLength:
|
14
|
+
Max: 140
|
15
|
+
|
16
|
+
Metrics/MethodLength:
|
17
|
+
Max: 20
|
18
|
+
|
19
|
+
Style/SpecialGlobalVars:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Style/StringLiterals:
|
23
|
+
EnforcedStyle: double_quotes
|
24
|
+
ConsistentQuotesInMultiline: false
|
25
|
+
|
26
|
+
Style/ClassAndModuleChildren:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Style/ModuleFunction:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
Style/FrozenStringLiteralComment:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
Style/Documentation:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
Style/MutableConstant:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
Style/FormatStringToken:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
Style/Lambda:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
Style/SymbolArray:
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
Style/FormatString:
|
51
|
+
Enabled: false
|
52
|
+
|
53
|
+
Style/PercentLiteralDelimiters:
|
54
|
+
Enabled: false
|
55
|
+
|
56
|
+
Lint/MissingCopEnableDirective:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
Style/NumericPredicate:
|
60
|
+
Enabled: false
|
61
|
+
|
62
|
+
Style/RegexpLiteral:
|
63
|
+
Enabled: false
|
64
|
+
|
65
|
+
Style/ClassVars:
|
66
|
+
Enabled: false
|
67
|
+
|
68
|
+
Style/ConditionalAssignment:
|
69
|
+
Enabled: false
|
70
|
+
|
71
|
+
Style/IfUnlessModifier:
|
72
|
+
Enabled: false
|
73
|
+
|
74
|
+
Style/PerlBackrefs:
|
75
|
+
Enabled: false
|
76
|
+
|
77
|
+
Style/TrailingUnderscoreVariable:
|
78
|
+
Enabled: false
|
79
|
+
|
80
|
+
Style/StderrPuts:
|
81
|
+
Enabled: false
|
82
|
+
|
83
|
+
Style/NonNilCheck:
|
84
|
+
Enabled: false
|
85
|
+
|
86
|
+
Metrics/ParameterLists:
|
87
|
+
Enabled: false
|
88
|
+
|
89
|
+
Style/StringLiteralsInInterpolation:
|
90
|
+
Enabled: false
|
91
|
+
|
92
|
+
Style/DoubleNegation:
|
93
|
+
Enabled: false
|
94
|
+
|
95
|
+
Style/ParallelAssignment:
|
96
|
+
Enabled: false
|
data/.tm_properties
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
excludeDirectories = "{_build,coverage,assets/node_modules,node_modules,deps,db,cover,priv/static,storage,github,vendor,arena,}"
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in {gemname}.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
# --- Development and test dependencies ------------------------------
|
7
|
+
|
8
|
+
group :development, :test do
|
9
|
+
gem 'rake', '~> 11'
|
10
|
+
gem 'rspec', '~> 3.7'
|
11
|
+
# gem 'rubocop', '~> 0.61.1'
|
12
|
+
gem 'simplecov', '~> 0'
|
13
|
+
gem 'byebug'
|
14
|
+
end
|
data/Makefile
ADDED
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# simple-service – a pretty simple and somewhat abstract service description
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
data/bin/bundle
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'bundle' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "rubygems"
|
12
|
+
|
13
|
+
m = Module.new do
|
14
|
+
module_function
|
15
|
+
|
16
|
+
def invoked_as_script?
|
17
|
+
File.expand_path($0) == File.expand_path(__FILE__)
|
18
|
+
end
|
19
|
+
|
20
|
+
def env_var_version
|
21
|
+
ENV["BUNDLER_VERSION"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def cli_arg_version
|
25
|
+
return unless invoked_as_script? # don't want to hijack other binstubs
|
26
|
+
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
27
|
+
bundler_version = nil
|
28
|
+
update_index = nil
|
29
|
+
ARGV.each_with_index do |a, i|
|
30
|
+
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
31
|
+
bundler_version = a
|
32
|
+
end
|
33
|
+
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
34
|
+
bundler_version = $1 || ">= 0.a"
|
35
|
+
update_index = i
|
36
|
+
end
|
37
|
+
bundler_version
|
38
|
+
end
|
39
|
+
|
40
|
+
def gemfile
|
41
|
+
gemfile = ENV["BUNDLE_GEMFILE"]
|
42
|
+
return gemfile if gemfile && !gemfile.empty?
|
43
|
+
|
44
|
+
File.expand_path("../../Gemfile", __FILE__)
|
45
|
+
end
|
46
|
+
|
47
|
+
def lockfile
|
48
|
+
lockfile =
|
49
|
+
case File.basename(gemfile)
|
50
|
+
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
|
51
|
+
else "#{gemfile}.lock"
|
52
|
+
end
|
53
|
+
File.expand_path(lockfile)
|
54
|
+
end
|
55
|
+
|
56
|
+
def lockfile_version
|
57
|
+
return unless File.file?(lockfile)
|
58
|
+
lockfile_contents = File.read(lockfile)
|
59
|
+
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
60
|
+
Regexp.last_match(1)
|
61
|
+
end
|
62
|
+
|
63
|
+
def bundler_version
|
64
|
+
@bundler_version ||= begin
|
65
|
+
env_var_version || cli_arg_version ||
|
66
|
+
lockfile_version || "#{Gem::Requirement.default}.a"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_bundler!
|
71
|
+
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
72
|
+
|
73
|
+
# must dup string for RG < 1.8 compatibility
|
74
|
+
activate_bundler(bundler_version.dup)
|
75
|
+
end
|
76
|
+
|
77
|
+
def activate_bundler(bundler_version)
|
78
|
+
if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
|
79
|
+
bundler_version = "< 2"
|
80
|
+
end
|
81
|
+
gem_error = activation_error_handling do
|
82
|
+
gem "bundler", bundler_version
|
83
|
+
end
|
84
|
+
return if gem_error.nil?
|
85
|
+
require_error = activation_error_handling do
|
86
|
+
require "bundler/version"
|
87
|
+
end
|
88
|
+
return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
89
|
+
warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
|
90
|
+
exit 42
|
91
|
+
end
|
92
|
+
|
93
|
+
def activation_error_handling
|
94
|
+
yield
|
95
|
+
nil
|
96
|
+
rescue StandardError, LoadError => e
|
97
|
+
e
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
m.load_bundler!
|
102
|
+
|
103
|
+
if m.invoked_as_script?
|
104
|
+
load Gem.bin_path("bundler", "bundle")
|
105
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
Bundler.require
|
5
|
+
require "simple-service"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# returns the comment for an action
|
2
|
+
class ::Simple::Service::Action::Comment
|
3
|
+
attr_reader :short
|
4
|
+
attr_reader :full
|
5
|
+
|
6
|
+
def self.extract(action:)
|
7
|
+
file, line = action.source_location
|
8
|
+
lines = Extractor.extract_comment_lines(file: file, before_line: line)
|
9
|
+
full = lines[2..-1].join("\n") if lines.length >= 2
|
10
|
+
new short: lines[0], full: full
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(short:, full:)
|
14
|
+
@short, @full = short, full
|
15
|
+
end
|
16
|
+
|
17
|
+
module Extractor
|
18
|
+
extend self
|
19
|
+
|
20
|
+
# reads the source \a file and turns each non-comment into :code and each comment
|
21
|
+
# into a string without the leading comment markup.
|
22
|
+
def parse_source(file)
|
23
|
+
@parsed_sources ||= {}
|
24
|
+
@parsed_sources[file] = _parse_source(file)
|
25
|
+
end
|
26
|
+
|
27
|
+
def _parse_source(file)
|
28
|
+
File.readlines(file).map do |line|
|
29
|
+
case line
|
30
|
+
when /^\s*# ?(.*)$/ then $1
|
31
|
+
when /^\s*end/ then :end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def extract_comment_lines(file:, before_line:)
|
37
|
+
parsed_source = parse_source(file)
|
38
|
+
|
39
|
+
# go down from before_line until we see a line which is either a comment
|
40
|
+
# or an :end. Note that the line at before_line-1 should be the first
|
41
|
+
# line of the method definition in question.
|
42
|
+
last_line = before_line - 1
|
43
|
+
last_line -= 1 while last_line >= 0 && !parsed_source[last_line]
|
44
|
+
|
45
|
+
first_line = last_line
|
46
|
+
first_line -= 1 while first_line >= 0 && parsed_source[first_line]
|
47
|
+
first_line += 1
|
48
|
+
|
49
|
+
comments = parsed_source[first_line..last_line]
|
50
|
+
if comments.include?(:end)
|
51
|
+
[]
|
52
|
+
else
|
53
|
+
parsed_source[first_line..last_line]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# rubocop:disable Metrics/AbcSize
|
2
|
+
|
3
|
+
module ::Simple::Service::Action::MethodReflection # :nodoc:
|
4
|
+
extend self
|
5
|
+
|
6
|
+
#
|
7
|
+
# returns an array with entries like the following:
|
8
|
+
#
|
9
|
+
# [ :key, name, default_value ]
|
10
|
+
# [ :keyreq, name [, nil ] ]
|
11
|
+
# [ :req, name [, nil ] ]
|
12
|
+
# [ :opt, name [, nil ] ]
|
13
|
+
# [ :rest, name [, nil ] ]
|
14
|
+
#
|
15
|
+
def parameters(service, method_id)
|
16
|
+
method = service.instance_method(method_id)
|
17
|
+
parameters = method.parameters
|
18
|
+
|
19
|
+
# method parameters with a :key mode are optional keyword arguments. We only
|
20
|
+
# support defaults for those - if there are none we abort here already.
|
21
|
+
keys = parameters.map { |mode, name| name if mode == :key }.compact
|
22
|
+
return parameters if keys.empty?
|
23
|
+
|
24
|
+
# We are now doing a fake call to the method, with a minimal viable set of
|
25
|
+
# arguments, to let the ruby runtime fill in default values for arguments.
|
26
|
+
# We do not, however, let the call complete. Instead we use a TracePoint to
|
27
|
+
# abort as soon as the method is called, and use the its binding to determine
|
28
|
+
# the default values.
|
29
|
+
|
30
|
+
fake_recipient = Object.new.extend(service)
|
31
|
+
fake_call_args = minimal_arguments(method)
|
32
|
+
|
33
|
+
trace_point = TracePoint.trace(:call) do |tp|
|
34
|
+
throw :received_fake_call, tp.binding if tp.defined_class == service && tp.method_id == method_id
|
35
|
+
end
|
36
|
+
|
37
|
+
bnd = catch(:received_fake_call) do
|
38
|
+
fake_recipient.send(method_id, *fake_call_args)
|
39
|
+
end
|
40
|
+
|
41
|
+
trace_point.disable
|
42
|
+
|
43
|
+
# extract default values from the received binding, and merge with the
|
44
|
+
# parameters array.
|
45
|
+
default_values = keys.each_with_object({}) do |key_parameter, hsh|
|
46
|
+
hsh[key_parameter] = bnd.local_variable_get(key_parameter)
|
47
|
+
end
|
48
|
+
|
49
|
+
parameters.map do |mode, name|
|
50
|
+
[mode, name, default_values[name]]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# returns a minimal Array of arguments, which is suitable for a call to the method
|
57
|
+
def minimal_arguments(method)
|
58
|
+
# Build an arguments array with holds all required parameters. The actual
|
59
|
+
# values for these arguments doesn't matter at all.
|
60
|
+
args = method.parameters.select { |mode, _name| mode == :req }
|
61
|
+
|
62
|
+
# Add a hash with all required keyword arguments
|
63
|
+
required_keyword_args = method.parameters.each_with_object({}) do |(mode, name), hsh|
|
64
|
+
hsh[name] = :anything if mode == :keyreq
|
65
|
+
end
|
66
|
+
args << required_keyword_args if required_keyword_args
|
67
|
+
|
68
|
+
args
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "method_reflection"
|
2
|
+
|
3
|
+
class ::Simple::Service::Action::Parameter
|
4
|
+
def self.reflect_on_method(service:, name:)
|
5
|
+
reflected_parameters = ::Simple::Service::Action::MethodReflection.parameters(service, name)
|
6
|
+
@parameters = reflected_parameters.map { |ary| new(*ary) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def keyword?
|
10
|
+
[:key, :keyreq].include? @kind
|
11
|
+
end
|
12
|
+
|
13
|
+
def anonymous?
|
14
|
+
[:req, :opt].include? @kind
|
15
|
+
end
|
16
|
+
|
17
|
+
def required?
|
18
|
+
[:req, :keyreq].include? @kind
|
19
|
+
end
|
20
|
+
|
21
|
+
def variadic?
|
22
|
+
@kind == :rest
|
23
|
+
end
|
24
|
+
|
25
|
+
def optional?
|
26
|
+
!required?
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :name
|
30
|
+
attr_reader :kind
|
31
|
+
|
32
|
+
# The parameter's default value (if any)
|
33
|
+
attr_reader :default_value
|
34
|
+
|
35
|
+
def initialize(kind, name, *default_value)
|
36
|
+
# The parameter list matches the values returned from MethodReflection.parameters,
|
37
|
+
# which has two or three entries: <tt>kind, name [ . default_value ]</tt>
|
38
|
+
|
39
|
+
expect! kind => [:req, :opt, :keyreq, :key, :rest]
|
40
|
+
expect! default_value.length => [0, 1]
|
41
|
+
|
42
|
+
@kind = kind
|
43
|
+
@name = name
|
44
|
+
@default_value = default_value[0]
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
2
|
+
# rubocop:disable Metrics/AbcSize
|
3
|
+
# rubocop:disable Metrics/MethodLength
|
4
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
5
|
+
|
6
|
+
module Simple::Service
|
7
|
+
class Action
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
require_relative "./action/comment"
|
12
|
+
require_relative "./action/parameter"
|
13
|
+
|
14
|
+
module Simple::Service
|
15
|
+
class Action
|
16
|
+
ArgumentError = ::Simple::Service::ArgumentError
|
17
|
+
|
18
|
+
IDENTIFIER_PATTERN = "[a-z][a-z0-9_]*"
|
19
|
+
IDENTIFIER_REGEXP = Regexp.compile("\\A#{IDENTIFIER_PATTERN}\\z")
|
20
|
+
|
21
|
+
# determines all services provided by the +service+ service module.
|
22
|
+
def self.enumerate(service:) # :nodoc:
|
23
|
+
service.public_instance_methods(false)
|
24
|
+
.grep(IDENTIFIER_REGEXP)
|
25
|
+
.each_with_object({}) { |name, hsh| hsh[name] = Action.new(service, name) }
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :service
|
29
|
+
attr_reader :name
|
30
|
+
|
31
|
+
# returns an Array of Parameter structures.
|
32
|
+
def parameters
|
33
|
+
@parameters ||= Parameter.reflect_on_method(service: service, name: name)
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(service, name)
|
37
|
+
@service = service
|
38
|
+
@name = name
|
39
|
+
|
40
|
+
parameters
|
41
|
+
end
|
42
|
+
|
43
|
+
def short_description
|
44
|
+
comment.short
|
45
|
+
end
|
46
|
+
|
47
|
+
def full_description
|
48
|
+
comment.full
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# returns a Comment object
|
54
|
+
#
|
55
|
+
# The comment object is extracted on demand on the first call.
|
56
|
+
def comment
|
57
|
+
@comment ||= Comment.extract(action: self)
|
58
|
+
end
|
59
|
+
|
60
|
+
public
|
61
|
+
|
62
|
+
def source_location
|
63
|
+
@service.instance_method(name).source_location
|
64
|
+
end
|
65
|
+
|
66
|
+
# build a service_instance and run the action, with arguments constructed from
|
67
|
+
# args_hsh and params_hsh.
|
68
|
+
def invoke(args, options)
|
69
|
+
args ||= {}
|
70
|
+
options ||= {}
|
71
|
+
|
72
|
+
# convert Array arguments into a Hash of named arguments. This is strictly
|
73
|
+
# necessary to be able to apply default value-based type conversions. (On
|
74
|
+
# the downside this also means we convert an array to a hash and then back
|
75
|
+
# into an array. This, however, should only be an issue for CLI based action
|
76
|
+
# invocations, because any other use case (that I can think of) should allow
|
77
|
+
# us to provide arguments as a Hash.
|
78
|
+
if args.is_a?(Array)
|
79
|
+
args = convert_argument_array_to_hash(args)
|
80
|
+
end
|
81
|
+
|
82
|
+
# [TODO] Type conversion according to default values.
|
83
|
+
args_ary = build_method_arguments(args, options)
|
84
|
+
|
85
|
+
service_instance = Object.new
|
86
|
+
service_instance.extend service
|
87
|
+
service_instance.public_send(@name, *args_ary)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
module IndifferentHashEx
|
93
|
+
def self.fetch(hsh, name)
|
94
|
+
missing_key!(name) unless hsh
|
95
|
+
|
96
|
+
hsh.fetch(name.to_sym) do
|
97
|
+
hsh.fetch(name.to_s) do
|
98
|
+
missing_key!(name)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.key?(hsh, name)
|
104
|
+
return false unless hsh
|
105
|
+
|
106
|
+
hsh.key?(name.to_sym) || hsh.key?(name.to_s)
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.missing_key!(name)
|
110
|
+
raise ArgumentError, "Missing argument in arguments hash: #{name}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
I = IndifferentHashEx
|
115
|
+
|
116
|
+
# returns an array of arguments suitable to be sent to the action method.
|
117
|
+
def build_method_arguments(args_hsh, params_hsh)
|
118
|
+
args = []
|
119
|
+
keyword_args = {}
|
120
|
+
|
121
|
+
parameters.each do |parameter|
|
122
|
+
if parameter.keyword?
|
123
|
+
if I.key?(params_hsh, parameter.name)
|
124
|
+
keyword_args[parameter.name] = I.fetch(params_hsh, parameter.name)
|
125
|
+
end
|
126
|
+
else
|
127
|
+
if parameter.variadic?
|
128
|
+
if I.key?(args_hsh, parameter.name)
|
129
|
+
args.concat(Array(I.fetch(args_hsh, parameter.name)))
|
130
|
+
end
|
131
|
+
else
|
132
|
+
if !parameter.optional? || I.key?(args_hsh, parameter.name)
|
133
|
+
args << I.fetch(args_hsh, parameter.name)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
unless keyword_args.empty?
|
140
|
+
args << keyword_args
|
141
|
+
end
|
142
|
+
|
143
|
+
args
|
144
|
+
end
|
145
|
+
|
146
|
+
def convert_argument_array_to_hash(ary)
|
147
|
+
# enumerate all of the action's anonymous arguments, trying to match them
|
148
|
+
# against the values in +ary+. If afterwards any arguments are still left
|
149
|
+
# in +ary+ they will be assigned to the variadic arguments array, which
|
150
|
+
# - if a variadic parameter is defined in this action - will be added to
|
151
|
+
# the hash as well.
|
152
|
+
hsh = {}
|
153
|
+
variadic_parameter_name = nil
|
154
|
+
|
155
|
+
parameters.each do |parameter|
|
156
|
+
next if parameter.keyword?
|
157
|
+
parameter_name = parameter.name
|
158
|
+
|
159
|
+
if parameter.variadic?
|
160
|
+
variadic_parameter_name = parameter_name
|
161
|
+
next
|
162
|
+
end
|
163
|
+
|
164
|
+
if ary.empty? && !parameter.optional?
|
165
|
+
raise ::Simple::Service::ArgumentError, "Missing #{parameter_name} parameter"
|
166
|
+
end
|
167
|
+
|
168
|
+
next if ary.empty?
|
169
|
+
|
170
|
+
hsh[parameter_name] = ary.shift
|
171
|
+
end
|
172
|
+
|
173
|
+
# Any arguments are left? Set variadic parameter, if defined, raise an error otherwise.
|
174
|
+
unless ary.empty?
|
175
|
+
unless variadic_parameter_name
|
176
|
+
raise ::Simple::Service::ArgumentError, "Extra parameters: #{ary.map(&:inspect).join(", ")}"
|
177
|
+
end
|
178
|
+
|
179
|
+
hsh[variadic_parameter_name] = ary
|
180
|
+
end
|
181
|
+
|
182
|
+
hsh
|
183
|
+
end
|
184
|
+
|
185
|
+
def full_name
|
186
|
+
"#{service}##{name}"
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_s
|
190
|
+
full_name
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Simple::Service
|
2
|
+
class Context
|
3
|
+
class ReadOnlyError < RuntimeError
|
4
|
+
def initialize(key)
|
5
|
+
super "Cannot overwrite existing context setting #{key.inspect}"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@hsh = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
@hsh[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(key, value)
|
20
|
+
existing_value = @hsh[key]
|
21
|
+
|
22
|
+
unless existing_value.nil? || existing_value == value
|
23
|
+
raise ReadOnlyError, key
|
24
|
+
end
|
25
|
+
|
26
|
+
@hsh[key] = value
|
27
|
+
end
|
28
|
+
|
29
|
+
IDENTIFIER_PATTERN = "[a-z][a-z0-9_]*"
|
30
|
+
IDENTIFIER_REGEXP = Regexp.compile("\\A#{IDENTIFIER_PATTERN}\\z")
|
31
|
+
ASSIGNMENT_REGEXP = Regexp.compile("\\A(#{IDENTIFIER_PATTERN})=\\z")
|
32
|
+
|
33
|
+
def method_missing(sym, *args, &block)
|
34
|
+
if block
|
35
|
+
super
|
36
|
+
elsif args.count == 0 && sym =~ IDENTIFIER_REGEXP
|
37
|
+
self[sym]
|
38
|
+
elsif args.count == 1 && sym =~ ASSIGNMENT_REGEXP
|
39
|
+
self[$1.to_sym] = args.first
|
40
|
+
else
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def respond_to_missing?(sym, include_private = false)
|
46
|
+
return true if IDENTIFIER_REGEXP.maptch?(sym)
|
47
|
+
return true if ASSIGNMENT_REGEXP.maptch?(sym)
|
48
|
+
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Simple::Service
|
2
|
+
module GemHelper
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def version(name)
|
6
|
+
spec = Gem.loaded_specs[name]
|
7
|
+
version = spec ? spec.version.to_s : "0.0.0"
|
8
|
+
version += "+unreleased" if !spec || unreleased?(spec)
|
9
|
+
version
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def unreleased?(spec)
|
15
|
+
return false unless defined?(Bundler::Source::Gemspec)
|
16
|
+
return true if spec.source.is_a?(::Bundler::Source::Gemspec)
|
17
|
+
return true if spec.source.is_a?(::Bundler::Source::Path)
|
18
|
+
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
VERSION = GemHelper.version "simple-service"
|
24
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Simple::Service
|
2
|
+
class ArgumentError < ::ArgumentError
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
require_relative "service/action"
|
7
|
+
require_relative "service/context"
|
8
|
+
|
9
|
+
# The Simple::Service module.
|
10
|
+
#
|
11
|
+
# To mark a target module as a service module one must include the
|
12
|
+
# Simple::Service module into the target module.
|
13
|
+
#
|
14
|
+
# This serves as a marker that this module is actually intended
|
15
|
+
# to be used as a service.
|
16
|
+
module Simple::Service
|
17
|
+
def self.included(klass)
|
18
|
+
klass.extend ClassMethods
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the current context.
|
22
|
+
def self.context
|
23
|
+
Thread.current[:"Simple::Service.context"]
|
24
|
+
end
|
25
|
+
|
26
|
+
# yields a block with a given context, and restores the previous context
|
27
|
+
# object afterwards.
|
28
|
+
def self.with_context(ctx, &block)
|
29
|
+
expect! ctx => [Simple::Service::Context, nil]
|
30
|
+
_ = block
|
31
|
+
|
32
|
+
old_ctx = Thread.current[:"Simple::Service.context"]
|
33
|
+
Thread.current[:"Simple::Service.context"] = ctx
|
34
|
+
yield
|
35
|
+
ensure
|
36
|
+
Thread.current[:"Simple::Service.context"] = old_ctx
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.action(service, name)
|
40
|
+
actions = self.actions(service)
|
41
|
+
actions[name] || begin
|
42
|
+
action_names = actions.keys.sort
|
43
|
+
informal = "service #{service} has these actions: #{action_names.map(&:inspect).join(", ")}"
|
44
|
+
raise "No such action #{name.inspect}; #{informal}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.service?(service)
|
49
|
+
service.is_a?(Module) && service.include?(self)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.actions(service)
|
53
|
+
raise ArgumentError, "service must be a #{self}" unless service?(service)
|
54
|
+
|
55
|
+
service.__simple_service_actions__
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.invoke(service, name, arguments, params, context: nil)
|
59
|
+
with_context(context) do
|
60
|
+
action(service, name).invoke(arguments, params)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module ClassMethods
|
65
|
+
# returns a Hash of actions provided by the service module.
|
66
|
+
def __simple_service_actions__ # :nodoc:
|
67
|
+
@__simple_service_actions__ ||= Action.enumerate(service: self)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Resolves a service by name. Returns nil if the name does not refer to a service,
|
72
|
+
# or the service module otherwise.
|
73
|
+
def self.resolve(str)
|
74
|
+
return unless str =~ /^[A-Z][A-Za-z0-9_]*(::[A-Z][A-Za-z0-9_]*)*$/
|
75
|
+
|
76
|
+
service = resolve_constant(str)
|
77
|
+
|
78
|
+
return unless service.is_a?(Module)
|
79
|
+
return unless service.include?(::Simple::Service)
|
80
|
+
|
81
|
+
service
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.resolve_constant(str)
|
85
|
+
const_get(str)
|
86
|
+
rescue NameError
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
end
|
data/lib/simple.rb
ADDED
data/log/.gitkeep
ADDED
File without changes
|
data/scripts/release
ADDED
data/scripts/release.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# -- helpers ------------------------------------------------------------------
|
4
|
+
|
5
|
+
def sys(cmd)
|
6
|
+
STDERR.puts "> #{cmd}"
|
7
|
+
system cmd
|
8
|
+
return true if $?.success?
|
9
|
+
|
10
|
+
STDERR.puts "> #{cmd} returned with exitstatus #{$?.exitstatus}"
|
11
|
+
$?.success?
|
12
|
+
end
|
13
|
+
|
14
|
+
def sys!(cmd, error: nil)
|
15
|
+
return true if sys(cmd)
|
16
|
+
STDERR.puts error if error
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def die!(msg)
|
21
|
+
STDERR.puts msg
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
ROOT = File.expand_path("#{File.dirname(__FILE__)}/..")
|
26
|
+
|
27
|
+
GEMSPEC = Dir.glob("*.gemspec").first || die!("Missing gemspec file.")
|
28
|
+
|
29
|
+
# -- Version reading and bumping ----------------------------------------------
|
30
|
+
|
31
|
+
module Version
|
32
|
+
extend self
|
33
|
+
|
34
|
+
VERSION_FILE = "#{Dir.getwd}/VERSION"
|
35
|
+
|
36
|
+
def read_version
|
37
|
+
version = File.exist?(VERSION_FILE) ? File.read(VERSION_FILE) : "0.0.1"
|
38
|
+
version.chomp!
|
39
|
+
raise "Invalid version number in #{VERSION_FILE}" unless version =~ /^\d+\.\d+\.\d+$/
|
40
|
+
version
|
41
|
+
end
|
42
|
+
|
43
|
+
def auto_version_bump
|
44
|
+
old_version_number = read_version
|
45
|
+
old = old_version_number.split('.')
|
46
|
+
|
47
|
+
current = old[0..-2] << old[-1].next
|
48
|
+
current.join('.')
|
49
|
+
end
|
50
|
+
|
51
|
+
def bump_version
|
52
|
+
next_version = ENV["VERSION"] || auto_version_bump
|
53
|
+
File.open(VERSION_FILE, "w") { |io| io.write next_version }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# -- check, bump, release a new gem version -----------------------------------
|
58
|
+
|
59
|
+
Dir.chdir ROOT
|
60
|
+
$BASE_BRANCH = ENV['BRANCH'] || 'master'
|
61
|
+
|
62
|
+
# ENV["BUNDLE_GEMFILE"] = "#{Dir.getwd}/Gemfile"
|
63
|
+
# sys! "bundle install"
|
64
|
+
|
65
|
+
sys! "git diff --exit-code > /dev/null", error: 'There are unstaged changes in your working directory'
|
66
|
+
sys! "git diff --cached --exit-code > /dev/null", error: 'There are staged but uncommitted changes'
|
67
|
+
|
68
|
+
sys! "git checkout #{$BASE_BRANCH}"
|
69
|
+
sys! "git pull"
|
70
|
+
|
71
|
+
Version.bump_version
|
72
|
+
version = Version.read_version
|
73
|
+
|
74
|
+
sys! "git add VERSION"
|
75
|
+
sys! "git commit -m \"bump gem to v#{version}\""
|
76
|
+
sys! "git tag -a v#{version} -m \"Tag #{version}\""
|
77
|
+
|
78
|
+
sys! "gem build #{GEMSPEC}"
|
79
|
+
|
80
|
+
sys! "git push origin #{$BASE_BRANCH}"
|
81
|
+
sys! 'git push --tags --force'
|
82
|
+
sys! "gem push #{Dir.glob('*.gem').first}"
|
83
|
+
|
84
|
+
sys! "mkdir -p pkg"
|
85
|
+
sys! "mv *.gem pkg"
|
86
|
+
|
87
|
+
STDERR.puts <<-MSG
|
88
|
+
================================================================================
|
89
|
+
Thank you for releasing a new gem version. You made my day.
|
90
|
+
================================================================================
|
91
|
+
MSG
|
data/scripts/stats
ADDED
data/scripts/watch
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# lib = File.expand_path('../lib', __FILE__)
|
2
|
+
# $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "simple-service"
|
6
|
+
gem.version = File.read("VERSION")
|
7
|
+
|
8
|
+
gem.authors = [ "radiospiel" ]
|
9
|
+
gem.email = "eno@radiospiel.org"
|
10
|
+
gem.homepage = "http://github.com/radiospiel/simple-service"
|
11
|
+
gem.summary = "Pretty simple and somewhat abstract service description"
|
12
|
+
|
13
|
+
gem.description = "Pretty simple and somewhat abstract service description"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = %w(lib)
|
18
|
+
|
19
|
+
# executables are used for development purposes only
|
20
|
+
gem.executables = []
|
21
|
+
|
22
|
+
gem.required_ruby_version = '~> 2.5'
|
23
|
+
|
24
|
+
gem.add_dependency "expectation", "~> 1"
|
25
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Simple::Service::Context do
|
4
|
+
let(:context) { Simple::Service::Context.new }
|
5
|
+
|
6
|
+
before do
|
7
|
+
context.one = 1
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "reading" do
|
11
|
+
it "returns a value if set" do
|
12
|
+
expect(context.one).to eq(1)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns nil if not set" do
|
16
|
+
expect(context.two).to be_nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "writing" do
|
21
|
+
it "sets a value if it does not exist yet" do
|
22
|
+
context.two = 2
|
23
|
+
expect(context.two).to eq(2)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "raises a ReadOnly error if the value exists and is not equal" do
|
27
|
+
expect {
|
28
|
+
context.one = 2
|
29
|
+
}.to raise_error(::Simple::Service::Context::ReadOnlyError)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "sets the value if it exists and is equal" do
|
33
|
+
context.one = 1
|
34
|
+
expect(context.one).to eq(1)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
ENV["RACK_ENV"] = "test"
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
|
4
|
+
require "byebug"
|
5
|
+
require "rspec"
|
6
|
+
|
7
|
+
Dir.glob("./spec/support/**/*.rb").sort.each { |path| load path }
|
8
|
+
|
9
|
+
require "simple/service"
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.run_all_when_everything_filtered = true
|
13
|
+
config.filter_run focus: (ENV["CI"] != "true")
|
14
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
15
|
+
config.order = "random"
|
16
|
+
config.example_status_persistence_file_path = ".rspec.data"
|
17
|
+
|
18
|
+
config.backtrace_exclusion_patterns << /spec\/support/
|
19
|
+
config.backtrace_exclusion_patterns << /spec_helper/
|
20
|
+
config.backtrace_exclusion_patterns << /database_cleaner/
|
21
|
+
|
22
|
+
# config.around(:each) do |example|
|
23
|
+
# example.run
|
24
|
+
# end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple-service
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- radiospiel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-11-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: expectation
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1'
|
27
|
+
description: Pretty simple and somewhat abstract service description
|
28
|
+
email: eno@radiospiel.org
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- ".gitignore"
|
34
|
+
- ".rubocop.yml"
|
35
|
+
- ".tm_properties"
|
36
|
+
- Gemfile
|
37
|
+
- Makefile
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- bin/bundle
|
42
|
+
- bin/console
|
43
|
+
- bin/rake
|
44
|
+
- bin/rspec
|
45
|
+
- lib/simple-service.rb
|
46
|
+
- lib/simple.rb
|
47
|
+
- lib/simple/service.rb
|
48
|
+
- lib/simple/service/action.rb
|
49
|
+
- lib/simple/service/action/comment.rb
|
50
|
+
- lib/simple/service/action/method_reflection.rb
|
51
|
+
- lib/simple/service/action/parameter.rb
|
52
|
+
- lib/simple/service/context.rb
|
53
|
+
- lib/simple/service/version.rb
|
54
|
+
- log/.gitkeep
|
55
|
+
- scripts/release
|
56
|
+
- scripts/release.rb
|
57
|
+
- scripts/stats
|
58
|
+
- scripts/watch
|
59
|
+
- simple-service.gemspec
|
60
|
+
- spec/simple/service/context_spec.rb
|
61
|
+
- spec/spec_helper.rb
|
62
|
+
- spec/support/004_simplecov.rb
|
63
|
+
homepage: http://github.com/radiospiel/simple-service
|
64
|
+
licenses: []
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '2.5'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubygems_version: 3.0.4
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: Pretty simple and somewhat abstract service description
|
85
|
+
test_files:
|
86
|
+
- spec/simple/service/context_spec.rb
|
87
|
+
- spec/spec_helper.rb
|
88
|
+
- spec/support/004_simplecov.rb
|