softwear-lib 3.1.5 → 3.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|