surikat 0.2.2
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 +15 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/inspectionProfiles/Project_Default.xml +16 -0
- data/.idea/misc.xml +7 -0
- data/.idea/modules.xml +8 -0
- data/.idea/surikat.iml +50 -0
- data/.idea/vcs.xml +6 -0
- data/.idea/workspace.xml +744 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +399 -0
- data/Rakefile +6 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/exe/surikat +234 -0
- data/lib/surikat.rb +421 -0
- data/lib/surikat/base_model.rb +35 -0
- data/lib/surikat/base_queries.rb +10 -0
- data/lib/surikat/base_type.rb +3 -0
- data/lib/surikat/configurations.rb +22 -0
- data/lib/surikat/new_app.rb +108 -0
- data/lib/surikat/routes.rb +67 -0
- data/lib/surikat/scaffold.rb +503 -0
- data/lib/surikat/session.rb +35 -0
- data/lib/surikat/session_manager.rb +92 -0
- data/lib/surikat/templates/.rspec.tmpl +1 -0
- data/lib/surikat/templates/.standalone_migrations.tmpl +6 -0
- data/lib/surikat/templates/Gemfile.tmpl +31 -0
- data/lib/surikat/templates/Rakefile.tmpl +2 -0
- data/lib/surikat/templates/aaa_queries.rb.tmpl +124 -0
- data/lib/surikat/templates/aaa_spec.rb.tmpl +151 -0
- data/lib/surikat/templates/application.yml.tmpl +14 -0
- data/lib/surikat/templates/base_aaa_model.rb.tmpl +28 -0
- data/lib/surikat/templates/base_model.rb.tmpl +6 -0
- data/lib/surikat/templates/base_spec.rb.tmpl +148 -0
- data/lib/surikat/templates/config.ru.tmpl +61 -0
- data/lib/surikat/templates/console.tmpl +14 -0
- data/lib/surikat/templates/crud_queries.rb.tmpl +105 -0
- data/lib/surikat/templates/database.yml.tmpl +26 -0
- data/lib/surikat/templates/hello_queries.rb.tmpl +19 -0
- data/lib/surikat/templates/hello_spec.rb.tmpl +39 -0
- data/lib/surikat/templates/routes.yml.tmpl +15 -0
- data/lib/surikat/templates/spec_helper.rb.tmpl +11 -0
- data/lib/surikat/templates/test_helper.rb.tmpl +30 -0
- data/lib/surikat/types.rb +45 -0
- data/lib/surikat/version.rb +3 -0
- data/lib/surikat/yaml_configurator.rb +18 -0
- data/surikat.gemspec +47 -0
- metadata +199 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
# This Surikat model class was generated automatically.
|
2
|
+
|
3
|
+
class User < Surikat::BaseModel
|
4
|
+
|
5
|
+
# Add your own validation rules, instance methods, relationships etc.
|
6
|
+
|
7
|
+
validates :email, uniqueness: true, presence: true
|
8
|
+
validates :hashed_password, presence: true
|
9
|
+
|
10
|
+
def password=(cleartext)
|
11
|
+
self.hashed_password = Digest::SHA256.hexdigest(cleartext)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.authenticate(args)
|
15
|
+
User.where(email: args['email'], hashed_password: Digest::SHA256.hexdigest(args['password'])).first
|
16
|
+
end
|
17
|
+
|
18
|
+
# We want to use +password+ instead of +hashed_password+ when we create the user
|
19
|
+
# This method is only used for in tests (+spec/user_spec.rb+).
|
20
|
+
def self.random_params
|
21
|
+
{
|
22
|
+
'email' => "some_#{SecureRandom.hex(5)}@email.com",
|
23
|
+
'password' => 'some password',
|
24
|
+
'roleids' => 'user,admin,superadmin'
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe % {class_name}Queries do
|
4
|
+
context "When testing the %{class_name} queries" do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
%{class_name}.destroy_all
|
8
|
+
@%{model_name} = %{class_name}.create_random
|
9
|
+
@more_%{model_name_plural} = (1..10).to_a.map {%{class_name}.create_random}
|
10
|
+
|
11
|
+
@expected_fields = [ %{columns_space} ]
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#queries" do
|
15
|
+
|
16
|
+
it "should retrieve an instance of %{class_name}" do
|
17
|
+
query = "
|
18
|
+
{ %{class_name}(id: #{@%{model_name}.id}) {
|
19
|
+
#{@expected_fields.join("\n")}
|
20
|
+
}
|
21
|
+
}"
|
22
|
+
response = Surikat::run query, {}
|
23
|
+
|
24
|
+
expected = {
|
25
|
+
"%{class_name}" => dates2string(@%{model_name}.attributes.slice(*@expected_fields))
|
26
|
+
}
|
27
|
+
|
28
|
+
expect(response).to eq expected
|
29
|
+
end
|
30
|
+
|
31
|
+
[ %{columns_space} ].each do |col|
|
32
|
+
it "should retrieve the #{col} of an instance of %{class_name}" do
|
33
|
+
query = "
|
34
|
+
{ %{class_name}(id: #{@%{model_name}.id}) {
|
35
|
+
#{col}
|
36
|
+
}
|
37
|
+
}"
|
38
|
+
response = Surikat::run query, {}
|
39
|
+
|
40
|
+
expected = {
|
41
|
+
"%{class_name}" => dates2string({
|
42
|
+
col => @%{model_name}.send(col)
|
43
|
+
})
|
44
|
+
}
|
45
|
+
|
46
|
+
expect(response).to eq expected
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should retrieve all instances of %{class_name}" do
|
51
|
+
query = "
|
52
|
+
{ %{class_name_plural}(q: \"id_lt=10000\") {
|
53
|
+
#{@expected_fields.join("\n")}
|
54
|
+
}
|
55
|
+
}"
|
56
|
+
response = Surikat::run query, {}
|
57
|
+
|
58
|
+
expected = {
|
59
|
+
"%{class_name_plural}" => ([ @%{model_name}] + @more_%{model_name_plural}).map {|r| dates2string(r.attributes.slice(*@expected_fields))}
|
60
|
+
}
|
61
|
+
|
62
|
+
expect(response).to eq expected
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
context "When testing the %{class_name} mutations" do
|
70
|
+
|
71
|
+
before(:all) do
|
72
|
+
Author.destroy_all
|
73
|
+
|
74
|
+
@expected_fields = [ %{columns_space} ]
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#mutations" do
|
78
|
+
|
79
|
+
it "should create an instance of %{class_name}" do
|
80
|
+
query = "
|
81
|
+
mutation %{class_name}($%{model_name}: %{class_name}Input) {
|
82
|
+
%{class_name}(%{model_name}: $%{model_name}) {
|
83
|
+
#{@expected_fields.join("\n")}
|
84
|
+
}
|
85
|
+
}
|
86
|
+
"
|
87
|
+
|
88
|
+
variables = {'%{model_name}' => %{class_name}.random_params}
|
89
|
+
|
90
|
+
response = Surikat::run query, variables
|
91
|
+
|
92
|
+
expect(response).to be_instance_of Hash
|
93
|
+
expect(response['%{class_name}']).to include variables['%{model_name}'].slice(*@expected_fields)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should update an instance of %{class_name}" do
|
97
|
+
%{model_name}_params = %{class_name}.random_params
|
98
|
+
%{model_name} = %{class_name}.create %{model_name}_params
|
99
|
+
|
100
|
+
new_%{model_name}_params = %{class_name}.random_params
|
101
|
+
|
102
|
+
# Step 1: Test that the update operation returns true
|
103
|
+
query = "
|
104
|
+
mutation Update%{class_name}($%{model_name}: %{class_name}Input) {
|
105
|
+
Update%{class_name}(%{model_name}: $%{model_name}) {
|
106
|
+
#{@expected_fields.join("\n")}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
"
|
110
|
+
|
111
|
+
params_plus = new_%{model_name}_params.merge({'id' => %{model_name}.id, 'created_at' => %{model_name}.created_at.to_s, 'updated_at' => %{model_name}.updated_at.to_s})
|
112
|
+
|
113
|
+
variables = {'%{model_name}' => params_plus}
|
114
|
+
|
115
|
+
response = Surikat::run query, variables
|
116
|
+
|
117
|
+
expect(response).to be_instance_of Hash
|
118
|
+
expect(response['Update%{class_name}']).to eq true
|
119
|
+
|
120
|
+
# Step 2: Test that the record has been updated
|
121
|
+
%{model_name}.reload
|
122
|
+
expect(%{model_name}.attributes.slice(*@expected_fields)).to eq params_plus.slice(*@expected_fields)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should destroy an instance of %{class_name}" do
|
126
|
+
%{model_name} = %{class_name}.create_random
|
127
|
+
|
128
|
+
# Step 1: Test that the destroy operation returns true
|
129
|
+
query = "
|
130
|
+
mutation Delete%{class_name}($id: ID) {
|
131
|
+
Delete%{class_name}(id: $id)
|
132
|
+
}
|
133
|
+
"
|
134
|
+
|
135
|
+
variables = {'id' => %{model_name}.id}
|
136
|
+
|
137
|
+
response = Surikat::run query, variables
|
138
|
+
|
139
|
+
expect(response).to be_instance_of Hash
|
140
|
+
expect(response['Delete%{class_name}']).to eq true
|
141
|
+
|
142
|
+
# Step 2: Test that the object actually doesn't exist
|
143
|
+
expect {%{model_name}.reload}.to raise_error ActiveRecord::RecordNotFound
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rack/app'
|
2
|
+
require 'oj'
|
3
|
+
require 'surikat'
|
4
|
+
|
5
|
+
Dir.glob('./config/initializers/*.rb').sort.each do |file|
|
6
|
+
require file
|
7
|
+
end
|
8
|
+
|
9
|
+
class %{app_name_capitalized} < Rack::App
|
10
|
+
|
11
|
+
get '/' do
|
12
|
+
app
|
13
|
+
end
|
14
|
+
|
15
|
+
post '/' do
|
16
|
+
app
|
17
|
+
end
|
18
|
+
|
19
|
+
def app
|
20
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT) unless ENV['RACK_ENV'] == 'production'
|
21
|
+
|
22
|
+
session_key = request.env['HTTP_SURIKAT']
|
23
|
+
|
24
|
+
# first, try to parse it as JSON
|
25
|
+
json_payload = Oj.load(payload) rescue nil
|
26
|
+
|
27
|
+
if json_payload
|
28
|
+
query, variables = json_payload['query'], json_payload['variables']
|
29
|
+
else # then, as parameters, either GET
|
30
|
+
if query = params['query']
|
31
|
+
variables = params['variables']
|
32
|
+
else # or POST
|
33
|
+
split_payload, query, variables = payload.split('&'), nil, nil
|
34
|
+
split_payload.to_a.each do |line|
|
35
|
+
split_line = line.split('=')
|
36
|
+
name, value = split_line.to_a[0], split_line.to_a[1, 1000].join('=')
|
37
|
+
|
38
|
+
query = CGI::unescape(value) if name == 'query'
|
39
|
+
variables = CGI::unescape(value) if name == 'variables'
|
40
|
+
end
|
41
|
+
|
42
|
+
variables = Oj.load(variables) if !variables.to_s.empty?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if query.to_s.strip.empty?
|
47
|
+
result = {'error' => 'Empty query'}
|
48
|
+
else
|
49
|
+
result = begin
|
50
|
+
Surikat::run query, variables, debug: false, session_key: session_key
|
51
|
+
rescue Exception => e
|
52
|
+
puts "Error running query #{query.inspect}: #{e.message}\n#{e.backtrace.join("\n")}"
|
53
|
+
{'error': e.message}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
Oj.dump result, mode: :compat
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
run %{app_name_capitalized}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
ENV['RACK_ENV'] ||= 'development'
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "surikat"
|
6
|
+
|
7
|
+
Dir.glob('./config/initializers/*.rb').sort.each do |file|
|
8
|
+
require file
|
9
|
+
end
|
10
|
+
|
11
|
+
puts "Welcome to the Surikat console. You are in the #{ENV['RACK_ENV']} environment."
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
@@ -0,0 +1,105 @@
|
|
1
|
+
=begin
|
2
|
+
This class was generated by the scaffold generator.
|
3
|
+
It contains methods to get, list, create, update and delete instances of %{class_name}.
|
4
|
+
Default routes are created for each of this method (use 'surikat list routes' or look inside config/routes.yml to see them).
|
5
|
+
Example queries/mutations can be found in the comments for each method.
|
6
|
+
Generated at:: %{time}
|
7
|
+
=end
|
8
|
+
class %{class_name}Queries < Surikat::BaseQueries
|
9
|
+
|
10
|
+
=begin
|
11
|
+
Description: Retrieve one %{class_name} instance by its id
|
12
|
+
Query Name: %{class_name}
|
13
|
+
|
14
|
+
Input: { 'id' => ID }
|
15
|
+
|
16
|
+
Output Type: %{class_name}
|
17
|
+
|
18
|
+
Query Example:
|
19
|
+
%{examples_get}
|
20
|
+
=end
|
21
|
+
def get
|
22
|
+
%{class_name}.where(id: arguments['id'])
|
23
|
+
end
|
24
|
+
|
25
|
+
=begin
|
26
|
+
Description: Retrieve all %{class_name} instances
|
27
|
+
Query Name: %{class_name_plural}
|
28
|
+
|
29
|
+
Input: { 'q' => String }
|
30
|
+
A Ransack selector (https://github.com/activerecord-hackery/ransack)
|
31
|
+
|
32
|
+
Output Type: [%{class_name}]
|
33
|
+
|
34
|
+
Query Example:
|
35
|
+
%{examples_list}
|
36
|
+
=end
|
37
|
+
def all
|
38
|
+
ransack = {}
|
39
|
+
CGI::parse(arguments['q']).each { |k, v| ransack[k] = v.first}
|
40
|
+
|
41
|
+
%{class_name}.ransack(ransack).result
|
42
|
+
end
|
43
|
+
|
44
|
+
=begin
|
45
|
+
Description: Create an instance of %{class_name} and return it; this is a mutation.
|
46
|
+
Mutation Name: %{class_name}
|
47
|
+
|
48
|
+
Input: { '%{class_name}' => {
|
49
|
+
%{input_type_detailed_no_id}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
Output Type: %{class_name}
|
54
|
+
|
55
|
+
Query Example:
|
56
|
+
%{examples_create}
|
57
|
+
|
58
|
+
Example Variables:
|
59
|
+
%{pretty_create_vars}
|
60
|
+
=end
|
61
|
+
def create
|
62
|
+
%{class_name}.create arguments['%{class_name_downcase}']
|
63
|
+
end
|
64
|
+
|
65
|
+
=begin
|
66
|
+
Description: Update an instance of %{class_name}; this is a mutation.
|
67
|
+
Query Name: Update%{class_name}
|
68
|
+
|
69
|
+
Input: { '%{class_name}' => {
|
70
|
+
%{input_type_detailed}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
Output Type: Boolean
|
75
|
+
|
76
|
+
Query Example:
|
77
|
+
%{examples_update}
|
78
|
+
|
79
|
+
Example Variables:
|
80
|
+
%{pretty_update_vars}
|
81
|
+
=end
|
82
|
+
def update
|
83
|
+
%{class_name}.find(arguments['%{class_name_downcase}']['id']).update(arguments['%{class_name_downcase}'].without('id'))
|
84
|
+
end
|
85
|
+
|
86
|
+
=begin
|
87
|
+
Description: Delete an instance of %{class_name}.
|
88
|
+
Query Name: Delete%{class_name}
|
89
|
+
|
90
|
+
Input: { 'id' => ID }
|
91
|
+
|
92
|
+
OutputType: Boolean
|
93
|
+
|
94
|
+
Query Example:
|
95
|
+
%{examples_delete}
|
96
|
+
|
97
|
+
Example Variables:
|
98
|
+
%{pretty_random_id}
|
99
|
+
=end
|
100
|
+
def delete
|
101
|
+
%{class_name}.where(id: arguments['id']).destroy_all
|
102
|
+
true
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# SQLite version 3.x
|
2
|
+
# gem install sqlite3
|
3
|
+
#
|
4
|
+
# Ensure the SQLite 3 gem is defined in your Gemfile
|
5
|
+
# gem 'sqlite3'
|
6
|
+
#
|
7
|
+
default: &default
|
8
|
+
adapter: sqlite3
|
9
|
+
pool: 5
|
10
|
+
timeout: 5000
|
11
|
+
|
12
|
+
development:
|
13
|
+
<<: *default
|
14
|
+
database: db/development.sqlite3
|
15
|
+
|
16
|
+
|
17
|
+
# Warning: The database defined as "test" will be erased and
|
18
|
+
# re-generated from your development database when you run tests.
|
19
|
+
# Do not set this db to the same as development or production!
|
20
|
+
test:
|
21
|
+
<<: *default
|
22
|
+
database: db/test.sqlite3
|
23
|
+
|
24
|
+
production:
|
25
|
+
<<: *default
|
26
|
+
database: db/production.sqlite3
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# This is a generated file.
|
2
|
+
# To test these queries, run 'rspec -f d spec/hello_spec.rb'
|
3
|
+
# To see example queries for them, use 'surikat exemplify HelloQueries hello'
|
4
|
+
# If you want to throw away this file, please remember to also delete the relevant routes and tests.
|
5
|
+
class HelloQueries < Surikat::BaseQueries
|
6
|
+
def hello
|
7
|
+
'world'
|
8
|
+
end
|
9
|
+
|
10
|
+
def show_session
|
11
|
+
session.to_h.inspect
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_session
|
15
|
+
key, val = "key_#{SecureRandom.hex(3)}", SecureRandom.hex(5)
|
16
|
+
session[key] = val
|
17
|
+
"#{key},#{val}"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe HelloQueries do
|
4
|
+
context "When greeting" do
|
5
|
+
describe "Surikat" do
|
6
|
+
it "should respond nicely" do
|
7
|
+
response = Surikat.run '{Hello}', {}
|
8
|
+
expect(response['Hello']).to eq 'world'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "When testing session management" do
|
14
|
+
describe "Surikat" do
|
15
|
+
it "should persist data in session between requests" do
|
16
|
+
query = "{AddSession}"
|
17
|
+
response = Surikat::run(query, {}, {session_key: 'abc'})
|
18
|
+
expect(response['AddSession']).to include ','
|
19
|
+
|
20
|
+
key, val = response['AddSession'].split(',')
|
21
|
+
query = '{ShowSession}'
|
22
|
+
response = Surikat::run(query, {}, {session_key: 'abc'})
|
23
|
+
expect(response['ShowSession']).to include val
|
24
|
+
expect(response['ShowSession']).to include key
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not persist data across different sessions" do
|
28
|
+
query = "{AddSession}"
|
29
|
+
response = Surikat::run(query, {}, {session_key: 'abc'})
|
30
|
+
expect(response['AddSession']).to include ','
|
31
|
+
|
32
|
+
_, val = response['AddSession'].split(',')
|
33
|
+
query = '{ShowSession}'
|
34
|
+
response = Surikat::run(query, {}, {session_key: 'def'})
|
35
|
+
expect(response['ShowSession']).to_not include val
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|