nayati 0.1.0

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 (81) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +79 -0
  4. data/Rakefile +32 -0
  5. data/app/models/nayati/application_record.rb +5 -0
  6. data/app/models/nayati/master_operation.rb +46 -0
  7. data/app/models/nayati/master_workflow.rb +67 -0
  8. data/app/models/nayati/name_based_constantable.rb +30 -0
  9. data/app/models/nayati/operation.rb +29 -0
  10. data/app/models/nayati/operation_configuration.rb +73 -0
  11. data/app/models/nayati/operation_implementer_base.rb +21 -0
  12. data/app/models/nayati/workflow.rb +170 -0
  13. data/config/operations.yml +3 -0
  14. data/db/migrate/20200127144219_create_workflow.rb +16 -0
  15. data/db/migrate/20200127144220_create_operation.rb +19 -0
  16. data/lib/generators/nayati/install/install_generator.rb +30 -0
  17. data/lib/generators/nayati/install/templates/create_operation.rb +19 -0
  18. data/lib/generators/nayati/install/templates/create_workflow.rb +16 -0
  19. data/lib/generators/nayati/install/templates/operations.yml +3 -0
  20. data/lib/generators/nayati/operation/operation_generator.rb +46 -0
  21. data/lib/generators/nayati/operation/templates/operation_template.txt +20 -0
  22. data/lib/generators/nayati/workflow/templates/nayati_service_template.txt +20 -0
  23. data/lib/generators/nayati/workflow/workflow_generator.rb +39 -0
  24. data/lib/nayati.rb +5 -0
  25. data/lib/nayati/engine.rb +12 -0
  26. data/lib/nayati/version.rb +3 -0
  27. data/lib/nayati/workflow_results/base.rb +18 -0
  28. data/lib/nayati/workflow_runner.rb +34 -0
  29. data/spec/dummy/Rakefile +6 -0
  30. data/spec/dummy/app/models/application_record.rb +3 -0
  31. data/spec/dummy/bin/bundle +3 -0
  32. data/spec/dummy/bin/rails +4 -0
  33. data/spec/dummy/bin/rake +4 -0
  34. data/spec/dummy/bin/setup +36 -0
  35. data/spec/dummy/bin/update +31 -0
  36. data/spec/dummy/bin/yarn +11 -0
  37. data/spec/dummy/config.ru +5 -0
  38. data/spec/dummy/config/application.rb +19 -0
  39. data/spec/dummy/config/boot.rb +5 -0
  40. data/spec/dummy/config/cable.yml +10 -0
  41. data/spec/dummy/config/database.yml +24 -0
  42. data/spec/dummy/config/environment.rb +5 -0
  43. data/spec/dummy/config/environments/development.rb +61 -0
  44. data/spec/dummy/config/environments/production.rb +94 -0
  45. data/spec/dummy/config/environments/test.rb +46 -0
  46. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  47. data/spec/dummy/config/initializers/assets.rb +14 -0
  48. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  49. data/spec/dummy/config/initializers/content_security_policy.rb +25 -0
  50. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  51. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  52. data/spec/dummy/config/initializers/inflections.rb +16 -0
  53. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  54. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  55. data/spec/dummy/config/locales/en.yml +33 -0
  56. data/spec/dummy/config/operations.yml +3 -0
  57. data/spec/dummy/config/puma.rb +34 -0
  58. data/spec/dummy/config/spring.rb +6 -0
  59. data/spec/dummy/config/storage.yml +34 -0
  60. data/spec/dummy/db/migrate/20200202111127_create_workflow.rb +16 -0
  61. data/spec/dummy/db/migrate/20200202111128_create_operation.rb +19 -0
  62. data/spec/dummy/db/schema.rb +36 -0
  63. data/spec/dummy/log/development.log +56 -0
  64. data/spec/dummy/package.json +5 -0
  65. data/spec/dummy/public/404.html +67 -0
  66. data/spec/dummy/public/422.html +67 -0
  67. data/spec/dummy/public/500.html +66 -0
  68. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  69. data/spec/dummy/public/apple-touch-icon.png +0 -0
  70. data/spec/dummy/public/favicon.ico +0 -0
  71. data/spec/factories/operations_factory.rb +6 -0
  72. data/spec/factories/workflow_factory.rb +7 -0
  73. data/spec/lib/decider/workflow_runner_spec.rb +59 -0
  74. data/spec/lib/decider_spec.rb +4 -0
  75. data/spec/models/decider/master_operation_spec.rb +26 -0
  76. data/spec/models/decider/master_workflow_spec.rb +28 -0
  77. data/spec/models/decider/operation_spec.rb +48 -0
  78. data/spec/models/decider/workflow_spec.rb +159 -0
  79. data/spec/rails_helper.rb +57 -0
  80. data/spec/spec_helper.rb +120 -0
  81. metadata +259 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0bbc6f00148eadad1b96f8e72de1c6c6d2b69572
4
+ data.tar.gz: 232650a7d477b49b88bf0ed33e275d7282d622bc
5
+ SHA512:
6
+ metadata.gz: ef068a11d3264a28b870a818ad9bd1791e2aca115662ebfe920e21ff05cf79a7ce56e856a3e60e8ab51995a950c5a4318578cfaee9325ba8eaed7480494291fb
7
+ data.tar.gz: 551b829e305bd72fb39deddf7793b1f7effcf840a8725c7d84f237a6fe33840162cc6612435d486d56e50c09d4ddea068e5f41105c4d7712ca9820430d1afac8
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Tanmay Tupe
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # Nayati
2
+ Nayati is a Rails engine that helps in creating a clean and maintainable multi tenant Rails application. Creating multitenant application with Nayati allows you to have models that do just database talking and business logic gets handled by a layer I like to call 'Operation layer'. The sequence in which these operations get executed for a tenant is put in database.
3
+
4
+ ## Installation
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'nayati'
9
+ ```
10
+
11
+ And then execute:
12
+ ```bash
13
+ $ bundle
14
+ ```
15
+
16
+ Or install it yourself as:
17
+ ```bash
18
+ $ gem install nayati
19
+ ```
20
+ First run the install generator
21
+ ```bash
22
+ $ rails generator nayati:install
23
+ ```
24
+
25
+ ## How to use
26
+ Nayati is meant to help in handling functional changes across tenants in your application.
27
+ Lets say you have a attendance system. Your company installs devices which have fingerprint scanning functionality. Your clients vary from
28
+ scools, gym, private companies. Now lets say you have 5 'operations' that can happen
29
+ in this workflow viz marking_attendace, notify_parents, give_extra_marks_for_consistency, notify_sitting_position, late_marking, notify_missing_registration
30
+ notify_number_of_late_marks
31
+
32
+ After installing, first generate a workflow e.g. attendance_management in example above.
33
+
34
+ ```bash
35
+ $ rails generate nayati:workflow attendance_management
36
+ ```
37
+
38
+ This will create a service class AttendanceManagementNayatiService which you are supposed to call in controller.
39
+
40
+ Service classes do following tasks
41
+ 1. Find which workflow is supposed to run based on information passed from controller.
42
+ 2. Create context that will be passed to every operation.
43
+ 3. Return result object. By convention, this will return instance Nayati::WorkflowResults::Base object. You can have your own result object. Just create a class in app/models/nayati/workflow_results/#{workflow_name}.rb and nayati will pass instance of this class to operation classes.
44
+
45
+ Next we will create our operation classes. RESPONSIBILITIES OF A SINGLE OPERATION CLASS SHOULD NOT CHANGE ACROSS TENANTS. Rather this is how I define operation class. It is that piece of functionality which does not change across tenant. to create an operation class belonging to a workflow run
46
+
47
+ ```bash
48
+ $ rails generate nayati:operation attendance_management marking_attendance
49
+ ```
50
+
51
+ Next we will configure a workflow for a tenant. Lets say we are going to identify this workflow by name 'tenant_1_attendance_marking'. This tenant is a school and needs only marking_attendance and late_marking piece of functionality and if marking_attendance fails, you are supposed to nottify to register first. Nayati comes with a method to configure a workflow in database.
52
+
53
+ workflow_hash corresponding to sequence mentioned in above paragraph would be
54
+ workflow_sequence_hash = {
55
+ name: 'marking_attendance',
56
+ workflow_identifier: 'tenant_1_attendance_marking',
57
+ initial_operation_name: 'marking_attendance',
58
+ operations: [
59
+ { name: 'marking_attendance', after_failure_operation_name: 'notify_missing_registration', after_success_operation_name: 'late_marking' },
60
+ { name: 'notify_missing_registration'},
61
+ { name: 'late_marking'}
62
+ ]
63
+ }
64
+
65
+ You can put this workflow in database by calling
66
+ ```ruby
67
+ Nayati::Workflow.create_or_update_from_workflow_json(workflow_sequence_hash)
68
+ ```
69
+
70
+ Next we will have a look at operation class
71
+ nayati:operation will generate operation class in app/nayati_operations/{workflow_name}/ directory which will automatically be initialized with operation_context that you created in nayati service class and result object when you call the 'call' method on service class.
72
+
73
+ By default nayati will first call to_fail? method of a operation class. If this method returns true, nayati will call perform_failure and next operation will be after_failure operation configured in database. If this method returns false, nayati will call perform method and next operation will be after_success operation configured in database. Nayati will stop when there is no operation to run.
74
+
75
+ ## Sample
76
+
77
+ You can find a sample multitenant application here.
78
+ ## License
79
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Nayati'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1,5 @@
1
+ module Nayati
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,46 @@
1
+ module Nayati
2
+ class MasterOperation
3
+ attr_reader :name
4
+ def initialize(master_workflow, operation_name)
5
+ @master_workflow = master_workflow
6
+ @name = operation_name
7
+ @edit_errors = []
8
+ end
9
+
10
+ def edit(new_name)
11
+ @new_name = ::Nayati::NameBasedConstantable.name_as_namespace(new_name.to_s)
12
+ @is_name_changed = true unless @new_name == @name
13
+ @valid_edit_called = false
14
+ end
15
+
16
+ def exists?
17
+ @master_workflow.operation_names.include?(@name)
18
+ end
19
+
20
+ def save
21
+ OperationConfiguration.add_operation_unless_present(@master_workflow.name, @name)
22
+ end
23
+
24
+ def valid_edit?
25
+ validate_name_for_edit if @is_name_changed
26
+ @edit_errors.empty?
27
+ @valid_edit_called = true
28
+ end
29
+
30
+ def update
31
+ raise 'Please check valid edit first' unless @valid_edit_called
32
+ OperationConfiguration.update_operation(@master_workflow.name, @name, @new_name)
33
+ end
34
+
35
+ def edit_error_message
36
+ @edit_errors.join(', ')
37
+ end
38
+
39
+ private
40
+
41
+ def validate_name_for_edit
42
+ @edit_errors << 'New name is not valid' unless @new_name.present?
43
+ @edit_errors << 'New name already exists' if @master_workflow.operation_names.include?(@new_name)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,67 @@
1
+ module Nayati
2
+ class MasterWorkflow
3
+ attr_reader :name
4
+ def initialize(name)
5
+ @name = name
6
+ @errors = []
7
+ @operation_addition_errors = []
8
+ @name_as_sym = @name.to_sym
9
+ end
10
+
11
+ def operation_names
12
+ Nayati::OperationConfiguration.operation_names(self.name)
13
+ end
14
+
15
+ def add_operation(operation_name)
16
+ Nayati::OperationConfiguration.add_operation_unless_present(self.name, operation_name)
17
+ end
18
+
19
+ def save!
20
+ Nayati::OperationConfiguration.add_workflow_unless_present(self.name)
21
+ end
22
+
23
+ def exists?
24
+ Nayati::OperationConfiguration.workflow_names.include?(@name_as_sym)
25
+ end
26
+
27
+ def valid?
28
+ validate_name
29
+ @errors.empty?
30
+ end
31
+
32
+ def valid_to_add_operation?(operation_name)
33
+ validate_operation_uniqueness(operation_name)
34
+ @operation_addition_errors.empty?
35
+ end
36
+
37
+ class << self
38
+ def all_workflow_names
39
+ Nayati::OperationConfiguration.workflow_names
40
+ end
41
+
42
+ end
43
+
44
+ def error_message
45
+ @errors.join(', ')
46
+ end
47
+
48
+ def operation_addition_error_message
49
+ @operation_addition_errors.join(', ')
50
+ end
51
+
52
+ private
53
+
54
+ def validate_operation_uniqueness(operation_name)
55
+ @operation_addition_errors << 'Already exists' if operation_exists?(operation_name)
56
+ end
57
+
58
+ def operation_exists?(operation_name)
59
+ operation_namespace = ::Nayati::NameBasedConstantable.name_as_namespace(operation_name.to_s)
60
+ operation_names.include?(operation_namespace)
61
+ end
62
+
63
+ def validate_name
64
+ @errors << 'Name already exists' if Nayati::OperationConfiguration.workflow_exists?(self.name)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,30 @@
1
+ module Nayati
2
+ module NameBasedConstantable
3
+ def camelcased_name
4
+ return "" if name.nil?
5
+ Nayati::NameBasedConstantable.name_as_constant(self.name)
6
+ end
7
+
8
+ def namespaced_name
9
+ return "" if name.nil?
10
+ Nayati::NameBasedConstantable.name_as_namespace(self.name)
11
+ end
12
+
13
+ def underscored_name
14
+ return "" if name.nil?
15
+ Nayati::NameBasedConstantable.underscored_name(name)
16
+ end
17
+
18
+ def self.name_as_constant(name)
19
+ name.to_s.split(' ').join('_').camelcase
20
+ end
21
+
22
+ def self.name_as_namespace(name)
23
+ name.to_s.downcase.split(' ').join('_')
24
+ end
25
+
26
+ def self.underscored_name(name)
27
+ name.to_s.underscore.split(' ').join('_')
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ module Nayati
2
+ class Operation < ApplicationRecord
3
+ include NameBasedConstantable
4
+
5
+ belongs_to :workflow
6
+ belongs_to :after_success_operation, class_name: "Operation", foreign_key: :after_success_operation_id, required: false
7
+ belongs_to :after_failure_operation, class_name: "Operation", foreign_key: :after_failure_operation_id, required: false
8
+ before_save :format_name
9
+
10
+
11
+ validates :name, presence: true
12
+
13
+ def operation_implementer_klass_name
14
+ "#{workflow.camelcased_name}NayatiWorkflow::#{self.camelcased_name}NayatiOperation"
15
+ end
16
+
17
+ def build_implementer(operation_context, result_object)
18
+ operation_implementer_klass_name.constantize.new(operation_context, result_object)
19
+ end
20
+
21
+ def details_hash
22
+ { name: self.name, after_success_operation: after_success_operation.try(:name), after_failure_operation: after_failure_operation.try(:name)}
23
+ end
24
+
25
+ def format_name
26
+ self.name = namespaced_name
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,73 @@
1
+ module Nayati
2
+ module OperationConfiguration
3
+ class << self
4
+ def configuration
5
+ @configuration ||= YAML.load(File.open(yaml_file_path)) || {}
6
+ end
7
+
8
+ def operation_names(workflow_name)
9
+ wf_name = ::Nayati::NameBasedConstantable.name_as_namespace(workflow_name.to_s)
10
+ (configuration[wf_name.to_sym] || []).map { |operation_conf| operation_conf.try(:[], :name) }
11
+ end
12
+
13
+ def add_workflow_unless_present(workflow_name)
14
+ wf_namespace = ::Nayati::NameBasedConstantable.name_as_namespace(workflow_name.to_s)
15
+ unless workflow_names.include?(wf_namespace.to_sym)
16
+ configuration[wf_namespace.to_sym] = []
17
+ write_configuration
18
+ end
19
+ end
20
+
21
+ def add_operation_unless_present(workflow_name, operation_name)
22
+ wf_namespace = ::Nayati::NameBasedConstantable.name_as_namespace(workflow_name.to_s)
23
+ op_namespace = ::Nayati::NameBasedConstantable.name_as_namespace(operation_name.to_s)
24
+
25
+ add_workflow_unless_present(wf_namespace)
26
+
27
+ unless operation_names(wf_namespace).include?(op_namespace)
28
+ configuration[wf_namespace.to_sym] << { name: op_namespace }
29
+ write_configuration
30
+ end
31
+ end
32
+
33
+ def workflow_exists?(workflow_name)
34
+ wf_namespace = ::Nayati::NameBasedConstantable.name_as_namespace(workflow_name.to_s)
35
+ workflow_names.include?(wf_namespace.to_sym)
36
+ end
37
+
38
+ def yaml_file_path
39
+ "#{Rails.root}/config/operations.yml"
40
+ end
41
+
42
+ def write_configuration
43
+ File.open(yaml_file_path, 'w') { |f| f.write configuration.to_yaml }
44
+ end
45
+
46
+ def workflow_names
47
+ configuration.keys
48
+ end
49
+
50
+ def update_operation(workflow_name, old_name, new_name)
51
+ op_conf = operation_configuration(workflow_name, old_name)
52
+
53
+ op_conf[:name] = ::Nayati::NameBasedConstantable.name_as_namespace(new_name.to_s)
54
+ write_configuration
55
+ end
56
+
57
+ private
58
+
59
+ def operation_configuration(workflow_name, operation_name)
60
+ wf_namespace = ::Nayati::NameBasedConstantable.name_as_namespace(workflow_name.to_s)
61
+ op_namespace = ::Nayati::NameBasedConstantable.name_as_namespace(operation_name.to_s)
62
+
63
+ wf_conf = workflow_configuration(wf_namespace)
64
+ wf_conf.detect { |operation| operation[:name] == op_namespace }
65
+
66
+ end
67
+
68
+ def workflow_configuration(workflow_namespace)
69
+ configuration[workflow_namespace.to_sym]
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,21 @@
1
+ module Nayati
2
+ class OperationImplementerBase
3
+ attr_accessor :operation_context
4
+ def initialize(operation_context)
5
+ @operation_context = operation_context
6
+ end
7
+
8
+ def to_fail?
9
+ puts "#{__callee__} called on #{self.class.name}"
10
+ false
11
+ end
12
+
13
+ def perform_failure
14
+ puts "#{__callee__} called on #{self.class.name}"
15
+ end
16
+
17
+ def perform
18
+ puts "#{__callee__} called on #{self.class.name}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,170 @@
1
+ require 'pry'
2
+ require 'nayati/workflow_results/base'
3
+ module Nayati
4
+ class Workflow < ApplicationRecord
5
+ include NameBasedConstantable
6
+
7
+ attr_accessor :context, :initial_operation_name
8
+
9
+ has_many :operations
10
+ after_initialize :set_initial_creation_flag
11
+ belongs_to :initial_operation, class_name: 'Operation', optional: true
12
+ after_create :create_initial_operation
13
+
14
+ validates :name, presence: true
15
+ validates :identifier, uniqueness: true
16
+ validates :initial_operation, presence: true, unless: :initial_creation_flag_set?
17
+ validates :initial_operation_name, presence: { message: 'Please set initial_operation_name'}, if: :initial_creation_flag_set?
18
+
19
+ def name_space
20
+ name.parameterize.underscore
21
+ end
22
+
23
+ def klass_name
24
+ name_space.camelcase + 'Workflow'
25
+ end
26
+
27
+ def klass
28
+ raise ContextNotSetError.new unless context.present?
29
+ klass_name.constantize.new(context)
30
+ end
31
+
32
+ def specific_result_obj_klass_constant_name
33
+ "Nayati::WorkflowResults::#{klass_name}"
34
+ end
35
+
36
+ def result_obj
37
+ result_class = begin
38
+ Module.const_get(specific_result_obj_klass_constant_name)
39
+ rescue NameError
40
+ ::Nayati::WorkflowResults::Base
41
+ end
42
+ result_class.new
43
+ end
44
+
45
+ def entire_workflow_as_hash
46
+ {
47
+ name: self.name,
48
+ initial_operation_name: self.initial_operation.try(:name),
49
+ workflow_identifier: self.identifier,
50
+ operations: operations.map { |operation| operation.details_hash }
51
+ }
52
+ end
53
+
54
+ class << self
55
+ def name_selection_options
56
+ Nayati::OperationConfiguration.workflow_names.map { |name| [name, name] }
57
+ end
58
+
59
+ # Workflow json has following structure
60
+ # {
61
+ # name: 'workflow_name'
62
+ # initial_operation_name: 'operation_name',
63
+ # workflow_identifier: 'workflow_identifier',
64
+ # operations: [
65
+ # { name: 'operation_name', after_failure_operation_name: 'afo_name', after_success_operation_name: 'aso_name' },
66
+ # .
67
+ # .
68
+ # .
69
+
70
+ # ]
71
+ # }
72
+
73
+ def create_or_update_from_workflow_json(workflow_hash)
74
+ ActiveRecord::Base.transaction do
75
+ workflow_name = workflow_hash[:name]
76
+ workflow_identifier = workflow_hash[:workflow_identifier]
77
+ initial_operation_name = workflow_hash[:initial_operation_name]
78
+
79
+ wf = find_or_initialize_by(name: workflow_name.to_s, identifier: workflow_identifier)
80
+ wf.initial_operation_name = initial_operation_name
81
+ wf.save!
82
+
83
+ initial_operation_record = wf.operations.find_or_initialize_by(name: initial_operation_name)
84
+ initial_operation_record.save!
85
+
86
+ workflow_hash[:operations].each do |operation_hash|
87
+ operation_name = operation_hash[:name].to_s
88
+ operation_record = wf.operations.find_or_initialize_by(name: operation_name)
89
+
90
+ after_failure_operation_name = operation_hash[:after_failure_operation_name].to_s
91
+ if after_failure_operation_name.present?
92
+ after_failure_operation_record = wf.operations.find_or_initialize_by(name: after_failure_operation_name)
93
+ after_failure_operation_record.save!
94
+ operation_record.after_failure_operation = after_failure_operation_record
95
+ end
96
+
97
+ after_success_operation_name = operation_hash[:after_success_operation_name].to_s
98
+ if after_success_operation_name.present?
99
+ after_success_operation_record = wf.operations.find_or_initialize_by(name: after_success_operation_name)
100
+ after_success_operation_record.save!
101
+ operation_record.after_success_operation = after_success_operation_record
102
+ end
103
+
104
+ operation_record.save!
105
+ end
106
+ # puts "---------------- printing operation sequences ----------------"
107
+ # wf.print_sequences
108
+ # puts "-----------------------------------------------------------------"
109
+ end
110
+ end
111
+ end
112
+
113
+ private
114
+
115
+ def configuration
116
+ @configuration ||= OperationConfiguration.new
117
+ end
118
+
119
+ def set_initial_creation_flag
120
+ @creation_flag = true
121
+ end
122
+
123
+ def initial_creation_flag_set?
124
+ @creation_flag
125
+ end
126
+
127
+ def create_initial_operation
128
+ op = self.operations.new(name: initial_operation_name)
129
+ op.save
130
+ self.initial_operation = op
131
+ self.save
132
+ end
133
+
134
+ def assign_from_creation_params(params)
135
+
136
+ end
137
+
138
+
139
+ # class Master
140
+ # attr_reader :name, :name_as_namespace, :operations
141
+ # def initialize(workflow_name)
142
+ # @name = @workflow_name
143
+ # @operations = operations_from_configuration(workflow_name)
144
+ # @name_as_namespace = Nayati::NameBasedConstantable.name_as_namespace(workflow_name)
145
+ # end
146
+
147
+ class << self
148
+ def assign_from_creation_params(params)
149
+ wf = self.new(name: params[:name])
150
+ wf.initial_operation_name = params[:initial_operation_name]
151
+ wf
152
+ end
153
+ end
154
+
155
+ # private
156
+
157
+ # def operations_from_configuration(workflow_name)
158
+ # configuration[@name_as_namespace]
159
+ # end
160
+
161
+ # def configuration
162
+ # @configuration ||= ( YAML.load(File.open("#{Rails.root}/config/operations.yml")) || {} )
163
+ # end
164
+ # end
165
+
166
+ class ContextNotSetError < StandardError
167
+ end
168
+
169
+ end
170
+ end