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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.idea/.rakeTasks +7 -0
  4. data/.idea/inspectionProfiles/Project_Default.xml +16 -0
  5. data/.idea/misc.xml +7 -0
  6. data/.idea/modules.xml +8 -0
  7. data/.idea/surikat.iml +50 -0
  8. data/.idea/vcs.xml +6 -0
  9. data/.idea/workspace.xml +744 -0
  10. data/.rspec +2 -0
  11. data/.travis.yml +5 -0
  12. data/Gemfile +6 -0
  13. data/LICENSE.txt +21 -0
  14. data/README.md +399 -0
  15. data/Rakefile +6 -0
  16. data/bin/console +11 -0
  17. data/bin/setup +8 -0
  18. data/exe/surikat +234 -0
  19. data/lib/surikat.rb +421 -0
  20. data/lib/surikat/base_model.rb +35 -0
  21. data/lib/surikat/base_queries.rb +10 -0
  22. data/lib/surikat/base_type.rb +3 -0
  23. data/lib/surikat/configurations.rb +22 -0
  24. data/lib/surikat/new_app.rb +108 -0
  25. data/lib/surikat/routes.rb +67 -0
  26. data/lib/surikat/scaffold.rb +503 -0
  27. data/lib/surikat/session.rb +35 -0
  28. data/lib/surikat/session_manager.rb +92 -0
  29. data/lib/surikat/templates/.rspec.tmpl +1 -0
  30. data/lib/surikat/templates/.standalone_migrations.tmpl +6 -0
  31. data/lib/surikat/templates/Gemfile.tmpl +31 -0
  32. data/lib/surikat/templates/Rakefile.tmpl +2 -0
  33. data/lib/surikat/templates/aaa_queries.rb.tmpl +124 -0
  34. data/lib/surikat/templates/aaa_spec.rb.tmpl +151 -0
  35. data/lib/surikat/templates/application.yml.tmpl +14 -0
  36. data/lib/surikat/templates/base_aaa_model.rb.tmpl +28 -0
  37. data/lib/surikat/templates/base_model.rb.tmpl +6 -0
  38. data/lib/surikat/templates/base_spec.rb.tmpl +148 -0
  39. data/lib/surikat/templates/config.ru.tmpl +61 -0
  40. data/lib/surikat/templates/console.tmpl +14 -0
  41. data/lib/surikat/templates/crud_queries.rb.tmpl +105 -0
  42. data/lib/surikat/templates/database.yml.tmpl +26 -0
  43. data/lib/surikat/templates/hello_queries.rb.tmpl +19 -0
  44. data/lib/surikat/templates/hello_spec.rb.tmpl +39 -0
  45. data/lib/surikat/templates/routes.yml.tmpl +15 -0
  46. data/lib/surikat/templates/spec_helper.rb.tmpl +11 -0
  47. data/lib/surikat/templates/test_helper.rb.tmpl +30 -0
  48. data/lib/surikat/types.rb +45 -0
  49. data/lib/surikat/version.rb +3 -0
  50. data/lib/surikat/yaml_configurator.rb +18 -0
  51. data/surikat.gemspec +47 -0
  52. metadata +199 -0
@@ -0,0 +1,14 @@
1
+ # Application Configuration
2
+ development:
3
+ session:
4
+ storage: file
5
+
6
+ test:
7
+ session:
8
+ storage: file
9
+ file: tmp/test_session_store
10
+
11
+ production:
12
+ session:
13
+ storage: redis
14
+ url: localhost
@@ -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,6 @@
1
+ # This Surikat model class was generated automatically.
2
+ class %{class_name} < Surikat::BaseModel
3
+
4
+ # Add your own validation rules, instance methods, relationships etc.
5
+
6
+ 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