softwear-lib 3.1.5 → 3.3.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +35 -34
- data/app/controllers/softwear/error_reports_controller.rb +5 -0
- data/app/controllers/softwear/spec_dump_controller.rb +22 -0
- data/app/mailers/error_report_mailer.rb +9 -5
- data/bin/softwear +4 -44
- data/bin/softwear-capture +225 -0
- data/bin/softwear-update +47 -0
- data/bin/sw +11 -0
- data/lib/softwear/auth/standard_model.rb +19 -1
- data/lib/softwear/auth/stubbed_model.rb +5 -0
- data/lib/softwear/auth/token_authentication.rb +2 -2
- data/lib/softwear/engine.rb +1 -1
- data/lib/softwear/error_catcher.rb +20 -6
- data/lib/softwear/lib.rb +17 -14
- data/lib/softwear/library/light_server.rb +119 -95
- data/lib/softwear/library/spec.rb +1 -1
- data/lib/softwear/library/spec_dump.rb +143 -0
- data/lib/softwear/library/version.rb +1 -1
- data/lib/softwear/templates/captured_spec.rb.liquid +17 -0
- data/softwear-lib.gemspec +3 -0
- metadata +53 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 061c8fef433bb6d6ba33834ed5e769907dd54722
|
4
|
+
data.tar.gz: ff88cfad72f88e87eca1360c9cba5ae0df211e9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 644e51672e6d4517e81f28bb1bfdf666b1fd6abcdf32b95378a1bf07dbda5030fafd1a5a3bb64553cd4d5da781d2039cc4d2aefb229c738d46f58d428a7ba4f5
|
7
|
+
data.tar.gz: 941059409053e3f121070319f85c7b5f8c14d6b8d04cde00cd17a94ad6172a69c0566b98c5446489890ac5295d7f0598b38ba80fcc11585c586e4fb9beb15e65
|
data/README.md
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# Softwear::Lib
|
2
2
|
|
3
|
-
TODO: Write a gem description
|
4
|
-
|
5
3
|
## Installation
|
6
4
|
|
7
5
|
Add this line to your application's Gemfile:
|
@@ -12,17 +10,29 @@ gem 'softwear-lib'
|
|
12
10
|
|
13
11
|
And then execute:
|
14
12
|
|
15
|
-
|
13
|
+
```bash
|
14
|
+
$ bundle install
|
15
|
+
```
|
16
16
|
|
17
|
-
|
17
|
+
# Usage
|
18
18
|
|
19
|
-
|
19
|
+
## Database capture
|
20
|
+
This feature will capture a generous subset of the production (or development)
|
21
|
+
database into a spec. This serves 2 main purposes:
|
20
22
|
|
21
|
-
|
23
|
+
1. to quickly convert hand-tested data into a spec without writing complex factories, and
|
24
|
+
2. to isolate and begin testing a faulty set of live data before fully understanding the problem
|
22
25
|
|
23
|
-
To
|
24
|
-
|
25
|
-
|
26
|
+
To run:
|
27
|
+
```bash
|
28
|
+
bundle exec softwear capture
|
29
|
+
```
|
30
|
+
Then, follow the prompts. You will be asked for model name, record ID,
|
31
|
+
which spec file to append, and a few optional identifiers for the
|
32
|
+
generated spec.
|
33
|
+
|
34
|
+
|
35
|
+
## Local authentication
|
26
36
|
<code>$ AUTH_SERVER=false bundle exec rails s</code>
|
27
37
|
<br />
|
28
38
|
<br />
|
@@ -31,11 +41,11 @@ Uses data from local users.yml.
|
|
31
41
|
|
32
42
|
Expected format for users.yml:
|
33
43
|
|
34
|
-
|
35
|
-
user's email:
|
36
|
-
first_name: user's first name
|
37
|
-
last_name: user's last name
|
38
|
-
profile_picture: local
|
44
|
+
```yaml
|
45
|
+
"user's email":
|
46
|
+
first_name: "user's first name"
|
47
|
+
last_name: "user's last name"
|
48
|
+
profile_picture: local/file/location
|
39
49
|
roles:
|
40
50
|
- role1
|
41
51
|
- role2
|
@@ -44,34 +54,25 @@ user's email:
|
|
44
54
|
- right1
|
45
55
|
- right2
|
46
56
|
- rightn
|
47
|
-
|
57
|
+
```
|
48
58
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
59
|
+
## Error handling
|
60
|
+
```ruby
|
61
|
+
# In ApplicationController
|
62
|
+
include Softwear::ErrorCatcher
|
53
63
|
helper Softwear::Auth::EmailsHelper
|
54
|
-
|
64
|
+
```
|
55
65
|
to your application controller.
|
56
|
-
|
57
|
-
|
66
|
+
|
67
|
+
|
58
68
|
For error styling and animation add:
|
59
|
-
|
60
|
-
|
69
|
+
|
70
|
+
```css
|
61
71
|
*= require animate/animate
|
62
72
|
|
63
73
|
.box-info-error{
|
64
74
|
background: "your desired color here";
|
65
75
|
}
|
66
|
-
|
76
|
+
```
|
67
77
|
to your application.css file.
|
68
78
|
|
69
|
-
|
70
|
-
|
71
|
-
## Contributing
|
72
|
-
|
73
|
-
1. Fork it ( https://github.com/[my-github-username]/softwear-lib/fork )
|
74
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
75
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
76
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
77
|
-
5. Create a new Pull Request
|
@@ -3,7 +3,12 @@ module Softwear
|
|
3
3
|
skip_before_action :authenticate_user! rescue nil
|
4
4
|
before_action :assign_current_user, only: :email_report
|
5
5
|
|
6
|
+
controller = self
|
6
7
|
helper Softwear::Engine.helpers do
|
8
|
+
define_method :current_user do
|
9
|
+
controller.instance_variable_get(:@current_user)
|
10
|
+
end
|
11
|
+
|
7
12
|
def method_missing(name, *args, &block)
|
8
13
|
if main_app.respond_to?(name)
|
9
14
|
main_app.send(name, *args, &block)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Softwear
|
2
|
+
class SpecDumpController < ApplicationController
|
3
|
+
include Library::SpecDump
|
4
|
+
|
5
|
+
helper Softwear::Engine.helpers do
|
6
|
+
def method_missing(name, *args, &block)
|
7
|
+
if main_app.respond_to?(name)
|
8
|
+
main_app.send(name, *args, &block)
|
9
|
+
else
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def dump
|
16
|
+
type = params[:type].classify
|
17
|
+
id = params[:id].to_i
|
18
|
+
|
19
|
+
render json: spec_dump(type.constantize.find(id))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -64,12 +64,16 @@ class ErrorReportMailer < ActionMailer::Base
|
|
64
64
|
private
|
65
65
|
|
66
66
|
def app_name
|
67
|
+
name = Figaro.env.hub_app_name
|
68
|
+
return name if name.present?
|
69
|
+
|
67
70
|
case Rails.application.class.parent.to_s
|
68
|
-
when "CrmSoftwearcrmCom" then return "
|
69
|
-
when "RetailPublishing" then return "
|
70
|
-
when "SoftwearProduction" then return "
|
71
|
-
when "SoftwearHub" then return "
|
72
|
-
when "AnnarborteesFba" then return "
|
71
|
+
when "CrmSoftwearcrmCom" then return "SoftWEAR CRM"
|
72
|
+
when "RetailPublishing" then return "SoftWEAR Mockbot"
|
73
|
+
when "SoftwearProduction" then return "SoftWEAR Production"
|
74
|
+
when "SoftwearHub" then return "SoftWEAR Hub"
|
75
|
+
when "AnnarborteesFba" then return "SoftWEAR Fba"
|
76
|
+
when "SoftwearRetail" then return "SoftWEAR Retail"
|
73
77
|
when "AnnarborteesWww" then return "Ann Arbor Tees WWW"
|
74
78
|
else
|
75
79
|
return "Unknown App"
|
data/bin/softwear
CHANGED
@@ -1,51 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
2
|
require 'softwear/lib'
|
4
3
|
|
5
|
-
if ARGV.length > 0
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
handler = nil
|
10
|
-
append_line = nil
|
11
|
-
|
12
|
-
injected_gems = false
|
13
|
-
|
14
|
-
ignore_line = lambda do |line|
|
15
|
-
if line.include? Softwear::Lib::GEMFILE_CLOSER
|
16
|
-
gemfile << line
|
17
|
-
handler = append_line
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
append_line = lambda do |line|
|
22
|
-
gemfile << line
|
23
|
-
if line.include? Softwear::Lib::GEMFILE_OPENER
|
24
|
-
puts "Updating common gems"
|
25
|
-
gemfile << Softwear::Lib::COMMON_GEMS
|
26
|
-
injected_gems = true
|
27
|
-
handler = ignore_line
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
handler = append_line
|
32
|
-
old_gemfile.each_line do |line|
|
33
|
-
handler.call(line)
|
34
|
-
end
|
35
|
-
|
36
|
-
unless injected_gems
|
37
|
-
puts "Adding common gems - check for duplicates!"
|
38
|
-
gemfile << "\n" + Softwear::Lib::GEMFILE_OPENER + "\n"
|
39
|
-
gemfile << Softwear::Lib::COMMON_GEMS
|
40
|
-
gemfile << Softwear::Lib::GEMFILE_CLOSER + "\n"
|
41
|
-
end
|
4
|
+
if ARGV.length > 0
|
5
|
+
dir = File.dirname(__FILE__)
|
6
|
+
file = "#{dir}/softwear-#{ARGV[0]}"
|
42
7
|
|
43
|
-
|
44
|
-
gemfile.each do |line|
|
45
|
-
file.write(line)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
puts "Done!"
|
8
|
+
exec "bundle exec #{file} #{ARGV[1..-1].map(&:inspect).join(' ')}"
|
49
9
|
else
|
50
10
|
puts "Run `softwear update` to update your gemfile's common dependencies"
|
51
11
|
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'softwear/library/spec_dump'
|
3
|
+
require 'tty/prompt'
|
4
|
+
require 'tty/spinner'
|
5
|
+
require 'pastel'
|
6
|
+
require 'liquid'
|
7
|
+
include Softwear::Library::SpecDump
|
8
|
+
|
9
|
+
LIB_DIR = File.expand_path File.join(File.dirname(__FILE__), '..')
|
10
|
+
PROMPT = TTY::Prompt.new
|
11
|
+
PASTEL = Pastel.new
|
12
|
+
|
13
|
+
class Capture
|
14
|
+
attr_reader :model_name
|
15
|
+
attr_reader :model
|
16
|
+
attr_reader :ids
|
17
|
+
attr_reader :records
|
18
|
+
attr_reader :data
|
19
|
+
attr_reader :capture_name
|
20
|
+
attr_reader :capture_path
|
21
|
+
attr_reader :capture_fullpath
|
22
|
+
attr_reader :spec_file
|
23
|
+
attr_reader :spec_desc
|
24
|
+
attr_reader :spec_fullpath
|
25
|
+
attr_reader :whitelist
|
26
|
+
attr_accessor :step
|
27
|
+
attr_reader :complete
|
28
|
+
@@steps = []
|
29
|
+
|
30
|
+
def self.step(method_name)
|
31
|
+
@@steps << method_name
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.steps
|
35
|
+
@@steps
|
36
|
+
end
|
37
|
+
|
38
|
+
def human_step
|
39
|
+
step.to_s.humanize
|
40
|
+
end
|
41
|
+
|
42
|
+
def step_index
|
43
|
+
self.class.steps.index(step)
|
44
|
+
end
|
45
|
+
|
46
|
+
def prompt
|
47
|
+
PROMPT
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
@step = @@steps.first
|
52
|
+
@complete = false
|
53
|
+
end
|
54
|
+
|
55
|
+
def increment_step
|
56
|
+
@step = @@steps[@@steps.index(@step) + 1]
|
57
|
+
if @step == nil
|
58
|
+
@complete = true
|
59
|
+
end
|
60
|
+
@step
|
61
|
+
end
|
62
|
+
|
63
|
+
def decrement_step
|
64
|
+
@step = @@steps[@@steps.index(@step) - 1]
|
65
|
+
@step = @@steps.first if @step.nil?
|
66
|
+
@step
|
67
|
+
end
|
68
|
+
|
69
|
+
def done?
|
70
|
+
@step.nil?
|
71
|
+
end
|
72
|
+
|
73
|
+
def complete?
|
74
|
+
complete
|
75
|
+
end
|
76
|
+
|
77
|
+
def execute!
|
78
|
+
send(@step)
|
79
|
+
end
|
80
|
+
|
81
|
+
def ignored_regexps
|
82
|
+
return [] if @ignored.try(:strip).blank?
|
83
|
+
@ignored.split(',').map { |x| Regexp.new(x.strip) }
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
step def initialize_rails
|
88
|
+
ENV['RAILS_ENV'] ||= prompt.ask "RAILS_ENV?", default: "development"
|
89
|
+
puts "Okay one sec (loading rails environment)"
|
90
|
+
Kernel.require "#{Dir.pwd}/config/environment"
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
step def find_records
|
95
|
+
@model_name = (ENV['MODEL'] || prompt.ask("Model to pull?", default: @model_name)).classify
|
96
|
+
@model = model_name.constantize
|
97
|
+
@ids = prompt.ask("#{model_name} ID?", default: @ids.try(:join, ',')).split(',').map(&:strip)
|
98
|
+
|
99
|
+
@records = model.where(id: @ids)
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
step def ignore_models
|
104
|
+
@ignored = prompt.ask "Enter comma-separated regexps of associations to ignore"
|
105
|
+
whitelist_file = prompt.ask "Enter whitelist JSON file"
|
106
|
+
@whitelist = JSON.parse(IO.read(whitelist_file)) if whitelist_file.present?
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
step def capture_data
|
111
|
+
spinner = TTY::Spinner.new("[:spinner] :title", format: TTY::Formats::FORMATS.keys.sample)
|
112
|
+
|
113
|
+
@data = spec_dump(records, ignored_regexps, whitelist) do |x|
|
114
|
+
spinner.update title: "Loaded #{x.history.join(' -> ')}"
|
115
|
+
spinner.spin
|
116
|
+
end
|
117
|
+
puts "\nCapture complete!\n"
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
step def save_capture
|
122
|
+
FileUtils.mkdir_p "#{Dir.pwd}/spec/fixtures/captures"
|
123
|
+
|
124
|
+
@capture_name = prompt.ask "Enter a name for the capture",
|
125
|
+
default: @capture_name || "#{model_name.underscore}-#{ids.join('-')}_#{DateTime.now.strftime("%Y-%m-%d")}"
|
126
|
+
|
127
|
+
@capture_path = "spec/fixtures/captures/#{capture_name.underscore.dasherize}.json"
|
128
|
+
@capture_fullpath = "#{Dir.pwd}/#{capture_path}"
|
129
|
+
|
130
|
+
IO.write capture_fullpath, JSON.pretty_generate(data)
|
131
|
+
puts "Wrote capture to #{capture_fullpath}"
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
step def generate_spec
|
136
|
+
unless prompt.yes?("Generate spec?")
|
137
|
+
puts <<-END
|
138
|
+
let(:capture) { JSON.parse(IO.read(Rails.root.join(#{capture_path.inspect}))) }
|
139
|
+
|
140
|
+
before :each do
|
141
|
+
Softwear::Library::SpecDump.expand_spec_dump(capture)
|
142
|
+
end
|
143
|
+
END
|
144
|
+
return
|
145
|
+
end
|
146
|
+
|
147
|
+
@spec_file = prompt.ask "Which spec file should this be added to?",
|
148
|
+
default: @spec_file || "spec/features/#{model_name.underscore.pluralize}_spec.rb"
|
149
|
+
@spec_desc = prompt.ask "Enter a name for the spec.",
|
150
|
+
default: @spec_desc || "#{model_name} problems #{DateTime.now.strftime("%Y-%m-%d")}"
|
151
|
+
|
152
|
+
@spec_fullpath = "#{Dir.pwd}/#{spec_file}"
|
153
|
+
|
154
|
+
template = Liquid::Template.parse(
|
155
|
+
IO.read("#{LIB_DIR}/lib/softwear/templates/captured_spec.rb.liquid"),
|
156
|
+
error_mode: :strict
|
157
|
+
)
|
158
|
+
new_spec = template.render!({
|
159
|
+
feature_or_describe: spec_file.include?("features") ? "feature" : "describe",
|
160
|
+
scenario_or_specify: spec_file.include?("features") ? "scenario" : "specify",
|
161
|
+
record_ids: ids.inspect,
|
162
|
+
model_name: model_name,
|
163
|
+
model_name_singular: model_name.underscore,
|
164
|
+
model_name_plural: model_name.underscore.pluralize,
|
165
|
+
spec_desc_str: spec_desc.inspect,
|
166
|
+
capture_path_str: capture_path.inspect,
|
167
|
+
capture_name: capture_name
|
168
|
+
}.stringify_keys, {
|
169
|
+
strict_variables: true
|
170
|
+
})
|
171
|
+
|
172
|
+
|
173
|
+
# Insert the new spec content into the existing spec file
|
174
|
+
spec_lines = IO.readlines(spec_fullpath)
|
175
|
+
|
176
|
+
index_of_last_end = spec_lines.size - 1
|
177
|
+
index_of_last_end -= 1 while spec_lines[index_of_last_end] !~ /end/
|
178
|
+
|
179
|
+
spec_lines.insert(index_of_last_end, new_spec)
|
180
|
+
|
181
|
+
IO.write(spec_fullpath, spec_lines.join)
|
182
|
+
puts "Wrote generated spec content to the end of #{spec_fullpath}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
##################
|
188
|
+
# Script logic #
|
189
|
+
##################
|
190
|
+
|
191
|
+
cap = Capture.new
|
192
|
+
|
193
|
+
until cap.done?
|
194
|
+
begin
|
195
|
+
cap.execute!
|
196
|
+
cap.increment_step
|
197
|
+
|
198
|
+
rescue Exception => error
|
199
|
+
retry if error.is_a?(ActiveRecord::StatementInvalid) && error.message.include?("closed MySQL connection")
|
200
|
+
|
201
|
+
puts PASTEL.red "\nERROR! #{error.class.name} \"#{error.message}\""
|
202
|
+
puts error.backtrace.join("\n")
|
203
|
+
puts PASTEL.red "#{error.class} encountered during #{cap.human_step.inspect} step\n"
|
204
|
+
|
205
|
+
choice = PROMPT.expand("Continue execution?") do |q|
|
206
|
+
q.choice key: 'q', name: 'Abort', value: :abort
|
207
|
+
q.choice key: 'r', name: 'Retry', value: :retry
|
208
|
+
q.choice key: 'R', name: 'Reload Rails and Retry', value: :reload
|
209
|
+
q.choice key: 'b', name: 'Go back one step', value: :back
|
210
|
+
end
|
211
|
+
|
212
|
+
case choice
|
213
|
+
when :abort then cap.step = nil
|
214
|
+
when :retry then nil
|
215
|
+
when :back then cap.decrement_step
|
216
|
+
when :reload then cap.step = :initialize_rails
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
if cap.complete?
|
222
|
+
puts "DONE"
|
223
|
+
else
|
224
|
+
puts "ABORTED"
|
225
|
+
end
|
data/bin/softwear-update
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'softwear/lib'
|
4
|
+
|
5
|
+
old_gemfile = File.open('Gemfile').read.gsub(/\r\n?/, "\n")
|
6
|
+
gemfile = []
|
7
|
+
|
8
|
+
handler = nil
|
9
|
+
append_line = nil
|
10
|
+
|
11
|
+
injected_gems = false
|
12
|
+
|
13
|
+
ignore_line = lambda do |line|
|
14
|
+
if line.include? Softwear::Lib::GEMFILE_CLOSER
|
15
|
+
gemfile << line
|
16
|
+
handler = append_line
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
append_line = lambda do |line|
|
21
|
+
gemfile << line
|
22
|
+
if line.include? Softwear::Lib::GEMFILE_OPENER
|
23
|
+
puts "Updating common gems"
|
24
|
+
gemfile << Softwear::Lib::COMMON_GEMS
|
25
|
+
injected_gems = true
|
26
|
+
handler = ignore_line
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
handler = append_line
|
31
|
+
old_gemfile.each_line do |line|
|
32
|
+
handler.call(line)
|
33
|
+
end
|
34
|
+
|
35
|
+
unless injected_gems
|
36
|
+
puts "Adding common gems - check for duplicates!"
|
37
|
+
gemfile << "\n" + Softwear::Lib::GEMFILE_OPENER + "\n"
|
38
|
+
gemfile << Softwear::Lib::COMMON_GEMS
|
39
|
+
gemfile << Softwear::Lib::GEMFILE_CLOSER + "\n"
|
40
|
+
end
|
41
|
+
|
42
|
+
File.open('Gemfile', 'w') do |file|
|
43
|
+
gemfile.each do |line|
|
44
|
+
file.write(line)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
puts "Done!"
|
data/bin/sw
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'softwear/lib'
|
3
|
+
|
4
|
+
if ARGV.length > 0
|
5
|
+
dir = File.dirname(__FILE__)
|
6
|
+
file = "#{dir}/softwear-#{ARGV[0]}"
|
7
|
+
|
8
|
+
exec "bundle exec #{file} #{ARGV[1..-1].map(&:inspect).join(' ')}"
|
9
|
+
else
|
10
|
+
puts "Run `softwear update` to update your gemfile's common dependencies"
|
11
|
+
end
|
@@ -403,6 +403,16 @@ module Softwear
|
|
403
403
|
objects.each { |u| u.instance_variable_set(:@persisted, true) }
|
404
404
|
end
|
405
405
|
|
406
|
+
# ====================
|
407
|
+
# Returns array of all users in the given group
|
408
|
+
# ====================
|
409
|
+
def of_group(group_code)
|
410
|
+
json = validate_response query "ofgroup #{Figaro.env.hub_app_name} #{group_code}"
|
411
|
+
|
412
|
+
objects = JSON.parse(json).map(&method(:new))
|
413
|
+
objects.each { |u| u.instance_variable_set(:@persisted, true) }
|
414
|
+
end
|
415
|
+
|
406
416
|
# ====================
|
407
417
|
# Given a valid signin token:
|
408
418
|
# Returns the authenticated user for the given token
|
@@ -432,7 +442,7 @@ module Softwear
|
|
432
442
|
|
433
443
|
REMOTE_ATTRIBUTES = [
|
434
444
|
:id, :email, :first_name, :last_name,
|
435
|
-
:roles, :profile_picture_url,
|
445
|
+
:roles, :groups, :profile_picture_url,
|
436
446
|
:default_view, :rights
|
437
447
|
]
|
438
448
|
REMOTE_ATTRIBUTES.each(&method(:attr_accessor))
|
@@ -504,6 +514,14 @@ module Softwear
|
|
504
514
|
wanted_roles.any? { |r| @roles.include?(r.to_s) }
|
505
515
|
end
|
506
516
|
end
|
517
|
+
|
518
|
+
def group?(group)
|
519
|
+
if @groups.nil?
|
520
|
+
query("group #{Figaro.env.hub_app_name} #{id} #{group}") == 'yes'
|
521
|
+
else
|
522
|
+
@groups.include?(group)
|
523
|
+
end
|
524
|
+
end
|
507
525
|
end
|
508
526
|
end
|
509
527
|
end
|
@@ -103,6 +103,11 @@ users:
|
|
103
103
|
all.select { |u| !u.roles.nil? && roles.any? { |r| u.roles.include?(r) } }
|
104
104
|
end
|
105
105
|
|
106
|
+
def of_group(group_code)
|
107
|
+
roles = Array(roles).map(&:to_s)
|
108
|
+
all.select { |u| !u.groups.nil? && u.groups.include?(group_code) }
|
109
|
+
end
|
110
|
+
|
106
111
|
def auth(_token, _appname = nil)
|
107
112
|
signed_in = users_yml[:signed_in]
|
108
113
|
|
@@ -9,8 +9,8 @@ module Softwear
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def token_authenticate_user!
|
12
|
-
user_class = self.class.user_class || base_class.user_class || User
|
13
|
-
options = (self.class.token_auth_options || base_class.token_auth_options || {}).with_indifferent_access
|
12
|
+
user_class = self.class.user_class || try(:base_class).try(:user_class) || User
|
13
|
+
options = (self.class.token_auth_options || try(:base_class).try(:token_auth_options) || {}).with_indifferent_access
|
14
14
|
params_options = (options[:params] || {}).with_indifferent_access
|
15
15
|
headers_options = (options[:headers] || {}).with_indifferent_access
|
16
16
|
|
data/lib/softwear/engine.rb
CHANGED
@@ -41,10 +41,15 @@ module Softwear
|
|
41
41
|
params.each do |key, value|
|
42
42
|
new_value = value
|
43
43
|
|
44
|
-
case
|
45
|
-
when
|
46
|
-
|
47
|
-
|
44
|
+
case value
|
45
|
+
when Hash
|
46
|
+
new_value = filter_params(value)
|
47
|
+
else
|
48
|
+
case key.to_s
|
49
|
+
when /cc_number/ then new_value = "<FILTERED>"
|
50
|
+
when /cc_cvc/ then new_value = "<FILTERED>"
|
51
|
+
when /password/ then new_value = "<FILTERED>"
|
52
|
+
end
|
48
53
|
end
|
49
54
|
|
50
55
|
new_hash[key] = new_value
|
@@ -56,12 +61,21 @@ module Softwear
|
|
56
61
|
JSON.pretty_generate(filter_params(params)) + "|||" +
|
57
62
|
instance_variables
|
58
63
|
.reject { |v| /^@_/ =~ v.to_s || %i(@view_renderer @output_buffer @view_flow @error).include?(v) }
|
59
|
-
.map { |v| "#{v}: #{instance_variable_get(v).inspect}" }
|
64
|
+
.map { |v| "#{v}: #{instance_variable_get(v).inspect rescue '(ERROR)'}" }
|
60
65
|
.join("|||")
|
61
66
|
end
|
62
67
|
|
63
68
|
def layout_for_error
|
64
|
-
|
69
|
+
user = try(:current_user) || @current_user
|
70
|
+
partner = Figaro.env.partner_namespace
|
71
|
+
|
72
|
+
if user
|
73
|
+
"application"
|
74
|
+
elsif !partner.nil? && request.url.include?(partner)
|
75
|
+
"partners/application"
|
76
|
+
else
|
77
|
+
"no_overlay"
|
78
|
+
end
|
65
79
|
end
|
66
80
|
end
|
67
81
|
end
|
data/lib/softwear/lib.rb
CHANGED
@@ -1,19 +1,22 @@
|
|
1
|
-
|
2
|
-
require "softwear/
|
3
|
-
require "softwear/library/
|
4
|
-
require "softwear/
|
5
|
-
require "softwear/library/
|
6
|
-
|
7
|
-
require "softwear/
|
8
|
-
|
1
|
+
if (::Rails rescue false)
|
2
|
+
require "softwear/engine"
|
3
|
+
require "softwear/library/api_controller"
|
4
|
+
require "softwear/error_catcher"
|
5
|
+
require "softwear/library/version"
|
6
|
+
require "softwear/library/spec"
|
7
|
+
require "softwear/auth/helper"
|
8
|
+
require "softwear/auth/model"
|
9
|
+
require "softwear/auth/belongs_to_user"
|
10
|
+
require "softwear/auth/spec"
|
11
|
+
require "softwear/auth/token_authentication"
|
12
|
+
require "softwear/library/controller_authentication"
|
13
|
+
begin
|
14
|
+
require "softwear/library/enqueue"
|
15
|
+
rescue LoadError
|
16
|
+
end
|
9
17
|
end
|
10
18
|
|
11
|
-
require "softwear/library/
|
12
|
-
require "softwear/auth/helper"
|
13
|
-
require "softwear/auth/model"
|
14
|
-
require "softwear/auth/belongs_to_user"
|
15
|
-
require "softwear/auth/spec"
|
16
|
-
require "softwear/auth/token_authentication"
|
19
|
+
require "softwear/library/spec_dump"
|
17
20
|
|
18
21
|
module Softwear
|
19
22
|
module Library
|
@@ -1,121 +1,145 @@
|
|
1
1
|
require 'rbczmq'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
module Softwear
|
4
|
+
module Library
|
5
|
+
class LightServer
|
6
|
+
def self.test!(file_path)
|
7
|
+
Object.class_eval do
|
8
|
+
def start_server!(*args)
|
9
|
+
Thread.new do
|
10
|
+
Softwear::Library::LightServer.new.start_server!(*args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
load file_path
|
15
|
+
end
|
6
16
|
|
7
|
-
def
|
8
|
-
|
9
|
-
|
17
|
+
def split(string, limit = nil)
|
18
|
+
string.split(/\s+/, limit)
|
19
|
+
end
|
10
20
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
$stderr.puts "(could not send 'sorry' message: \"#{e.class} #{e.message}\")"
|
15
|
-
end
|
16
|
-
end
|
21
|
+
def report_error(rep, whole_command, error)
|
22
|
+
$stderr.puts "=== ERROR WHILE PROCESSING THE COMMAND \"#{whole_command}\" ===\n"\
|
23
|
+
"#{error.class.name}: #{error.message}\n#{error.backtrace.join("\n")}"
|
17
24
|
|
18
|
-
|
19
|
-
|
20
|
-
|
25
|
+
begin
|
26
|
+
rep.send "sorry" if rep
|
27
|
+
rescue StandardError => e
|
28
|
+
$stderr.puts "(could not send 'sorry' message: \"#{e.class} #{e.message}\")"
|
29
|
+
end
|
30
|
+
end
|
21
31
|
|
22
|
-
def
|
23
|
-
|
24
|
-
end
|
32
|
+
def dev_log(*a)
|
33
|
+
$stdout.puts(*a) #if Rails.env.development?
|
34
|
+
end
|
25
35
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
# =========================
|
30
|
-
# === Receive Format: =====
|
31
|
-
# Usually one string, like "yes", or "no".
|
32
|
-
# Returns "denied" if an unauthorized command was attempted.
|
33
|
-
# Returns "invalid" if an invalid command was attempted.
|
34
|
-
# Returns "sorry" if an error was raised while processing the command.
|
35
|
-
# Can be a json argument, often following "yes ".
|
36
|
-
# =========================
|
37
|
-
def start_server!(*args)
|
38
|
-
log "Connecting...!"
|
39
|
-
|
40
|
-
if args.size > 1
|
41
|
-
port = args.first
|
42
|
-
else
|
43
|
-
port = ENV['port'] || ENV['PORT'] || 2900
|
44
|
-
end
|
45
|
-
address = ENV['SOCKET_ADDR'] || "tcp://*"
|
36
|
+
def log(*a)
|
37
|
+
$stdout.puts(*a) #unless Rails.env.test?
|
38
|
+
end
|
46
39
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
40
|
+
# ==== Send Format: =======
|
41
|
+
# One line strings: "#{command} #{arg1} #{arg2} #{etc}"
|
42
|
+
# The last argument, depending on the command, may contain spaces (but usually does not need to)
|
43
|
+
# =========================
|
44
|
+
# === Receive Format: =====
|
45
|
+
# Usually one string, like "yes", or "no".
|
46
|
+
# Returns "denied" if an unauthorized command was attempted.
|
47
|
+
# Returns "invalid" if an invalid command was attempted.
|
48
|
+
# Returns "sorry" if an error was raised while processing the command.
|
49
|
+
# Can be a json argument, often following "yes ".
|
50
|
+
# =========================
|
51
|
+
def start_server!(*args)
|
52
|
+
log "Connecting...!"
|
53
|
+
|
54
|
+
if args.size > 1
|
55
|
+
port = args.first
|
56
|
+
else
|
57
|
+
port = ENV['port'] || ENV['PORT'] || 2900
|
58
|
+
end
|
59
|
+
address = ENV['SOCKET_ADDR'] || "tcp://*"
|
52
60
|
|
53
|
-
|
54
|
-
|
61
|
+
if address =~ /:$/
|
62
|
+
socket_address = address
|
63
|
+
else
|
64
|
+
socket_address = "#{address}:#{port}"
|
65
|
+
end
|
55
66
|
|
56
|
-
|
67
|
+
rep = zmq.bind(:REP, socket_address)
|
57
68
|
|
58
|
-
|
69
|
+
log "Ready! Using \"#{ActiveRecord::Base.connection.try(:current_database) || 'in-memory'}\" database"
|
59
70
|
|
60
|
-
|
61
|
-
begin
|
62
|
-
loop do
|
63
|
-
line_in = rep.recv
|
64
|
-
raise "Got nil response (ZMQ REP/REQ out of sync?)" if line_in.nil?
|
65
|
-
command, rest_of_command = split(line_in, 2)
|
71
|
+
commands = args.last
|
66
72
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
# once.
|
81
|
-
replied = false
|
82
|
-
reply = lambda do |msg|
|
83
|
-
if replied
|
84
|
-
raise "Reply sent twice"
|
73
|
+
loop do
|
74
|
+
begin
|
75
|
+
loop do
|
76
|
+
line_in = rep.recv
|
77
|
+
raise "Got nil response (ZMQ REP/REQ out of sync?)" if line_in.nil?
|
78
|
+
command, rest_of_command = split(line_in, 2)
|
79
|
+
|
80
|
+
before = Time.now
|
81
|
+
begin
|
82
|
+
command = commands[command.downcase.to_sym]
|
83
|
+
|
84
|
+
if command.nil?
|
85
|
+
log "Received invalid command: \"#{line_in}\""
|
85
86
|
else
|
86
|
-
|
87
|
-
|
87
|
+
dev_log "<== #{line_in}"
|
88
|
+
ActiveRecord::Base.connection_pool.with_connection do
|
89
|
+
|
90
|
+
# The ZMQ socket requires that a reply be send after every
|
91
|
+
# message -- so we pass a lambda for the client code to
|
92
|
+
# call to send a message and make sure it only happens
|
93
|
+
# once.
|
94
|
+
replied = false
|
95
|
+
reply = lambda do |msg|
|
96
|
+
if replied
|
97
|
+
raise "Reply sent twice"
|
98
|
+
else
|
99
|
+
rep.send(msg)
|
100
|
+
replied = true
|
101
|
+
end
|
102
|
+
end
|
103
|
+
instance_exec(reply, rest_of_command, &command)
|
104
|
+
|
105
|
+
if !replied
|
106
|
+
rep.send "noreply"
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
88
110
|
end
|
89
|
-
end
|
90
|
-
command.call(reply, rest_of_command)
|
91
111
|
|
92
|
-
|
93
|
-
rep
|
112
|
+
rescue StandardError => e
|
113
|
+
report_error(rep, line_in, e)
|
114
|
+
rescue Exception => e
|
115
|
+
report_error(rep, line_in, e)
|
116
|
+
break
|
94
117
|
end
|
118
|
+
after = Time.now
|
95
119
|
|
120
|
+
ms = (after - before) * 1000
|
121
|
+
dev_log %[(#{'%.2f' % ms}ms)]
|
122
|
+
dev_log ""
|
96
123
|
end
|
97
|
-
end
|
98
124
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
report_error(rep, line_in, e)
|
103
|
-
break
|
104
|
-
end
|
105
|
-
after = Time.now
|
125
|
+
rescue StandardError => error
|
126
|
+
$stderr.puts "=== ERROR -- RESTARTING SERVER ===\n"\
|
127
|
+
"#{error.class.name}: #{error.message}\n#{error.backtrace.join("\n")}"
|
106
128
|
|
107
|
-
|
108
|
-
|
109
|
-
|
129
|
+
rep.destroy
|
130
|
+
log "Reconnecting...!"
|
131
|
+
rep = zmq.bind(:REP, socket_address)
|
132
|
+
end
|
133
|
+
end
|
110
134
|
end
|
111
135
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
rep.destroy
|
117
|
-
log "Reconnecting...!"
|
118
|
-
rep = ctx.bind(:REP, socket_address)
|
136
|
+
def zmq
|
137
|
+
$zmq_context ||= ZMQ::Context.new
|
138
|
+
end
|
119
139
|
end
|
120
140
|
end
|
121
141
|
end
|
142
|
+
|
143
|
+
def start_server!(*args)
|
144
|
+
Softwear::Library::LightServer.new.start_server!(*args)
|
145
|
+
end
|
@@ -100,7 +100,7 @@ module Softwear::Library
|
|
100
100
|
old_scopes = page.instance_variable_get(:@scopes)
|
101
101
|
page.instance_variable_set(:@scopes, [nil])
|
102
102
|
|
103
|
-
find('input.select2-search__field').set(text)
|
103
|
+
find('input.select2-search__field[tabindex="0"]').set(text)
|
104
104
|
sleep options[:wait_before_click] if options[:wait_before_click]
|
105
105
|
result = first('li.select2-results__option')
|
106
106
|
if result.nil? || result.text == "No results found"
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Library
|
3
|
+
module SpecDump
|
4
|
+
Record = Struct.new(:object, :history) do
|
5
|
+
def model; object.class; end
|
6
|
+
end
|
7
|
+
|
8
|
+
def spec_dump(root, ignored_models = [], whitelist_array = nil)
|
9
|
+
types_recorded = {}
|
10
|
+
records_by_type = {}
|
11
|
+
record_queue = Queue.new
|
12
|
+
whitelist = whitelist_array ? whitelist_array.reduce({}) { |h,n| h.merge(n => true) } : Hash.new(true)
|
13
|
+
|
14
|
+
is_ignored = lambda do |name|
|
15
|
+
next true if ignored_models.any? { |i| name =~ i }
|
16
|
+
next true if !whitelist[name]
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
# Begin dump routine. This will return true when we added a new entry.
|
21
|
+
dump = lambda do |record|
|
22
|
+
cache = (records_by_type[record.model.name] ||= {})
|
23
|
+
identifier = record.object.id
|
24
|
+
|
25
|
+
next false if cache[identifier].present?
|
26
|
+
|
27
|
+
attributes = {}
|
28
|
+
record.model.column_names.each do |col|
|
29
|
+
value = record.object[col]
|
30
|
+
if value.respond_to?(:iso8601)
|
31
|
+
# DateTimes don't serialize properly in attributes_before_type_cast
|
32
|
+
# for some reason, so we explicitly call to_s(:db) to make sure
|
33
|
+
# they can be loaded again correctly.
|
34
|
+
raw_value = value.to_s(:db)
|
35
|
+
else
|
36
|
+
raw_value = record.object.attributes_before_type_cast[col]
|
37
|
+
end
|
38
|
+
|
39
|
+
attributes[col] = raw_value
|
40
|
+
end
|
41
|
+
|
42
|
+
cache[identifier] = attributes
|
43
|
+
true
|
44
|
+
end
|
45
|
+
# end dump routine
|
46
|
+
|
47
|
+
# Begin actual dumping of records
|
48
|
+
Array(root).each do |record|
|
49
|
+
record_queue << Record.new(record, ["#{record.class.name}##{record.id}"])
|
50
|
+
end
|
51
|
+
|
52
|
+
while record_queue.present?
|
53
|
+
record = record_queue.pop
|
54
|
+
next if record.object.nil?
|
55
|
+
next unless record.model.respond_to?(:column_names)
|
56
|
+
|
57
|
+
# If dump returns false, that means we've already dumped this record
|
58
|
+
next unless dump.(record)
|
59
|
+
types_recorded[record.model.name] = true
|
60
|
+
|
61
|
+
yield record, types_recorded if block_given?
|
62
|
+
|
63
|
+
record.model.reflect_on_all_associations.each do |assoc|
|
64
|
+
next if assoc.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
65
|
+
|
66
|
+
next if is_ignored["#{record.model.name}##{assoc.name}"]
|
67
|
+
|
68
|
+
case assoc
|
69
|
+
when ActiveRecord::Reflection::BelongsToReflection
|
70
|
+
# A belongs_to association will never cause an infinite loop
|
71
|
+
record_queue << Record.new(
|
72
|
+
record.object.send(assoc.name),
|
73
|
+
record.history + ["#{record.model.name}##{record.object.id}##{assoc.name}"]
|
74
|
+
)
|
75
|
+
|
76
|
+
when ActiveRecord::Reflection::HasManyReflection
|
77
|
+
# A has_many association can cause an infinite loop, so we only
|
78
|
+
# process these if we've never seen the record type before.
|
79
|
+
#
|
80
|
+
# If there's a whitelist, no need to care about that
|
81
|
+
next if whitelist_array.blank? && types_recorded[assoc.klass.name]
|
82
|
+
|
83
|
+
record.object.send(assoc.name).each_with_index do |child, i|
|
84
|
+
next if child.nil?
|
85
|
+
record_queue << Record.new(
|
86
|
+
child,
|
87
|
+
record.history + ["#{record.model.name}##{record.object.id}##{assoc.name}[#{i}]"]
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
# end actual dumping of records
|
94
|
+
|
95
|
+
records_by_type
|
96
|
+
end
|
97
|
+
|
98
|
+
def expand_spec_dump(dump, use_outside_of_test = false)
|
99
|
+
if !Rails.env.test? && !use_outside_of_test
|
100
|
+
raise "Tried to call expand_spec_dump outside of test environment. "\
|
101
|
+
"If you really want to do this, pass `true` as the second parameter."
|
102
|
+
end
|
103
|
+
|
104
|
+
if ActiveRecord::Base.configurations[Rails.env]['adapter'].include?('sqlite')
|
105
|
+
insert_cmd = lambda do |model|
|
106
|
+
"INSERT OR REPLACE INTO #{model.table_name} (#{model.column_names.map { |c| "`#{c}`" }.join(', ')}) VALUES\n"
|
107
|
+
end
|
108
|
+
cmd_suffix = ->_{ "" }
|
109
|
+
else
|
110
|
+
insert_cmd = lambda do |model|
|
111
|
+
"INSERT INTO #{model.table_name} (#{model.column_names.map { |c| "`#{c}`" }.join(', ')}) VALUES\n"
|
112
|
+
end
|
113
|
+
cmd_suffix = lambda do |model|
|
114
|
+
"\nON DUPLICATE KEY UPDATE\n" +
|
115
|
+
model.column_names
|
116
|
+
.map { |col| "`#{col}` = VALUES(`#{col}`)" }
|
117
|
+
.join(",\n")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
dump.each do |class_name, entries|
|
122
|
+
model = class_name.constantize
|
123
|
+
sql = insert_cmd[model]
|
124
|
+
sanitize = model.connection.method(:quote)
|
125
|
+
|
126
|
+
sql += entries.map do |entry|
|
127
|
+
_id, attributes = entry
|
128
|
+
|
129
|
+
'(' +
|
130
|
+
model.column_names.map { |col| sanitize[attributes[col]] }.join(', ') +
|
131
|
+
')'
|
132
|
+
end.join(",\n")
|
133
|
+
|
134
|
+
sql += cmd_suffix[model]
|
135
|
+
|
136
|
+
model.connection.execute sql
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
extend self
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
{{feature_or_describe}} "CAPTURE: {{capture_name}}", captured: true do
|
3
|
+
let(:capture) { JSON.parse(IO.read(Rails.root.join({{capture_path_str}}))) }
|
4
|
+
let(:{{model_name_plural}}) { {{model_name}}.where(id: {{record_ids}}) }
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
Softwear::Library::SpecDump.expand_spec_dump(capture)
|
8
|
+
end
|
9
|
+
|
10
|
+
{{scenario_or_specify}} {{spec_desc_str}} do
|
11
|
+
visit edit_{{model_name_singular}}_path({{model_name_singular}})
|
12
|
+
|
13
|
+
# TODO fill in generated spec!
|
14
|
+
byebug
|
15
|
+
expect(false).to eq true
|
16
|
+
end
|
17
|
+
end
|
data/softwear-lib.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: softwear-lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.3.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nigel Baillie
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,12 +80,57 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: tty-prompt
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: tty-spinner
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: liquid
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
83
125
|
description:
|
84
126
|
email:
|
85
127
|
- nigel@annarbortees.com
|
86
128
|
executables:
|
87
129
|
- softwear
|
130
|
+
- softwear-capture
|
88
131
|
- softwear-deploy
|
132
|
+
- softwear-update
|
133
|
+
- sw
|
89
134
|
extensions: []
|
90
135
|
extra_rdoc_files: []
|
91
136
|
files:
|
@@ -100,6 +145,7 @@ files:
|
|
100
145
|
- app/assets/stylesheets/softwear.css
|
101
146
|
- app/controllers/softwear/application_controller.rb
|
102
147
|
- app/controllers/softwear/error_reports_controller.rb
|
148
|
+
- app/controllers/softwear/spec_dump_controller.rb
|
103
149
|
- app/helpers/softwear/application_helper.rb
|
104
150
|
- app/mailers/error_report_mailer.rb
|
105
151
|
- app/views/error_report_mailer/send_report.html.erb
|
@@ -108,7 +154,10 @@ files:
|
|
108
154
|
- app/views/softwear/errors/internal_server_error.html.erb
|
109
155
|
- app/views/softwear/errors/internal_server_error.js.erb
|
110
156
|
- bin/softwear
|
157
|
+
- bin/softwear-capture
|
111
158
|
- bin/softwear-deploy
|
159
|
+
- bin/softwear-update
|
160
|
+
- bin/sw
|
112
161
|
- config/routes.rb
|
113
162
|
- lib/softwear/auth/belongs_to_user.rb
|
114
163
|
- lib/softwear/auth/controller.rb
|
@@ -129,7 +178,9 @@ files:
|
|
129
178
|
- lib/softwear/library/enqueue.rb
|
130
179
|
- lib/softwear/library/light_server.rb
|
131
180
|
- lib/softwear/library/spec.rb
|
181
|
+
- lib/softwear/library/spec_dump.rb
|
132
182
|
- lib/softwear/library/version.rb
|
183
|
+
- lib/softwear/templates/captured_spec.rb.liquid
|
133
184
|
- softwear-lib.gemspec
|
134
185
|
- spec/spec_helper.rb
|
135
186
|
- vendor/assets/stylesheets/animate/animate.css
|