action_tracker_client 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e6552efeec467f77ebcdd5e12f42ea71f604549501d28197693931c7952ab1f5
4
+ data.tar.gz: 9d59e3d822664351982c2edf20745577cf37442907ce3d0a213815c128f1ce53
5
+ SHA512:
6
+ metadata.gz: 76f7c8f9293b8fe68d012f1a02314f7f3db256f2f8f36b16a48566a8928f6c27df1ec1531dbfc0e34319b3885859ebaa8466d6034590a315fc028c2f58d2de76
7
+ data.tar.gz: 9aaa18d8ef6fba934d1a3926ca3ccf9556db103430124015fd1e35ff1495e2430f364a9caf4d57a7299643a272677ef24daa496bc0df00231a11845389ae518e
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,55 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.5.5
3
+
4
+ Exclude:
5
+ - "Guardfile"
6
+ - "Rakefile"
7
+ - "bin/**/*"
8
+ - "spec/dummy/**/*"
9
+
10
+ ##################### Styles ##################################
11
+
12
+ Style/Documentation:
13
+ Enabled: false
14
+
15
+ Style/SymbolArray:
16
+ Enabled: false
17
+
18
+ Style/FrozenStringLiteralComment:
19
+ Enabled: false
20
+
21
+ Style/ClassAndModuleChildren:
22
+ Exclude:
23
+ - "app/controllers/*_controller.rb"
24
+ - "app/controllers/**/*_controller.rb"
25
+
26
+ #################### Lint ##################################
27
+
28
+ Lint/AmbiguousBlockAssociation:
29
+ Enabled: false
30
+
31
+ ##################### Metrics ##################################
32
+
33
+ Metrics/LineLength:
34
+ Max: 110
35
+
36
+ Metrics/ClassLength:
37
+ Max: 200
38
+
39
+ Metrics/ModuleLength:
40
+ Max: 200
41
+ Exclude:
42
+ - "**/*_spec.rb"
43
+
44
+ Metrics/BlockLength:
45
+ Max: 50
46
+ Exclude:
47
+ - "**/*_spec.rb"
48
+
49
+ ##################### Rails ##################################
50
+
51
+ Rails:
52
+ Enabled: true
53
+
54
+ Rails/SkipsModelValidations:
55
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5.5
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.4
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in action_tracker.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,136 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ action_tracker_client (0.0.1)
5
+ activemodel (>= 4.0)
6
+ activesupport (>= 4.0)
7
+ api_signature (~> 0.1.5)
8
+ httparty (~> 0.17.0)
9
+ model_auditor (~> 0.0.1)
10
+ virtus (~> 1.0, >= 1.0.5)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ activemodel (5.2.3)
16
+ activesupport (= 5.2.3)
17
+ activesupport (5.2.3)
18
+ concurrent-ruby (~> 1.0, >= 1.0.2)
19
+ i18n (>= 0.7, < 2)
20
+ minitest (~> 5.1)
21
+ tzinfo (~> 1.1)
22
+ addressable (2.6.0)
23
+ public_suffix (>= 2.0.2, < 4.0)
24
+ api_signature (0.1.5)
25
+ activesupport (>= 4.0)
26
+ rack (>= 1.6)
27
+ axiom-types (0.1.1)
28
+ descendants_tracker (~> 0.0.4)
29
+ ice_nine (~> 0.11.0)
30
+ thread_safe (~> 0.3, >= 0.3.1)
31
+ byebug (11.0.1)
32
+ coderay (1.1.2)
33
+ coercible (1.0.0)
34
+ descendants_tracker (~> 0.0.1)
35
+ concurrent-ruby (1.1.5)
36
+ crack (0.4.3)
37
+ safe_yaml (~> 1.0.0)
38
+ descendants_tracker (0.0.4)
39
+ thread_safe (~> 0.3, >= 0.3.1)
40
+ diff-lcs (1.3)
41
+ equalizer (0.0.11)
42
+ ffi (1.11.1)
43
+ formatador (0.2.5)
44
+ guard (2.15.0)
45
+ formatador (>= 0.2.4)
46
+ listen (>= 2.7, < 4.0)
47
+ lumberjack (>= 1.0.12, < 2.0)
48
+ nenv (~> 0.1)
49
+ notiffany (~> 0.0)
50
+ pry (>= 0.9.12)
51
+ shellany (~> 0.0)
52
+ thor (>= 0.18.1)
53
+ guard-compat (1.2.1)
54
+ guard-rspec (4.7.3)
55
+ guard (~> 2.1)
56
+ guard-compat (~> 1.1)
57
+ rspec (>= 2.99.0, < 4.0)
58
+ hashdiff (0.3.9)
59
+ httparty (0.17.0)
60
+ mime-types (~> 3.0)
61
+ multi_xml (>= 0.5.2)
62
+ i18n (1.6.0)
63
+ concurrent-ruby (~> 1.0)
64
+ ice_nine (0.11.2)
65
+ listen (3.1.5)
66
+ rb-fsevent (~> 0.9, >= 0.9.4)
67
+ rb-inotify (~> 0.9, >= 0.9.7)
68
+ ruby_dep (~> 1.2)
69
+ lumberjack (1.0.13)
70
+ method_source (0.9.2)
71
+ mime-types (3.2.2)
72
+ mime-types-data (~> 3.2015)
73
+ mime-types-data (3.2019.0331)
74
+ minitest (5.11.3)
75
+ model_auditor (0.0.1)
76
+ multi_xml (0.6.0)
77
+ nenv (0.3.0)
78
+ notiffany (0.1.1)
79
+ nenv (~> 0.1)
80
+ shellany (~> 0.0)
81
+ pry (0.12.2)
82
+ coderay (~> 1.1.0)
83
+ method_source (~> 0.9.0)
84
+ pry-byebug (3.7.0)
85
+ byebug (~> 11.0)
86
+ pry (~> 0.10)
87
+ public_suffix (3.1.0)
88
+ rack (2.0.7)
89
+ rake (10.5.0)
90
+ rb-fsevent (0.10.3)
91
+ rb-inotify (0.10.0)
92
+ ffi (~> 1.0)
93
+ rspec (3.8.0)
94
+ rspec-core (~> 3.8.0)
95
+ rspec-expectations (~> 3.8.0)
96
+ rspec-mocks (~> 3.8.0)
97
+ rspec-core (3.8.0)
98
+ rspec-support (~> 3.8.0)
99
+ rspec-expectations (3.8.3)
100
+ diff-lcs (>= 1.2.0, < 2.0)
101
+ rspec-support (~> 3.8.0)
102
+ rspec-mocks (3.8.0)
103
+ diff-lcs (>= 1.2.0, < 2.0)
104
+ rspec-support (~> 3.8.0)
105
+ rspec-support (3.8.0)
106
+ ruby_dep (1.5.0)
107
+ safe_yaml (1.0.5)
108
+ shellany (0.0.1)
109
+ thor (0.20.3)
110
+ thread_safe (0.3.6)
111
+ tzinfo (1.2.5)
112
+ thread_safe (~> 0.1)
113
+ virtus (1.0.5)
114
+ axiom-types (~> 0.1)
115
+ coercible (~> 1.0)
116
+ descendants_tracker (~> 0.0, >= 0.0.3)
117
+ equalizer (~> 0.0, >= 0.0.9)
118
+ webmock (3.5.1)
119
+ addressable (>= 2.3.6)
120
+ crack (>= 0.3.2)
121
+ hashdiff
122
+
123
+ PLATFORMS
124
+ ruby
125
+
126
+ DEPENDENCIES
127
+ action_tracker_client!
128
+ bundler (~> 1.16)
129
+ guard-rspec (~> 4.7, >= 4.7.3)
130
+ pry-byebug
131
+ rake (~> 10.0)
132
+ rspec (~> 3.0)
133
+ webmock (~> 3.5, >= 3.5.1)
134
+
135
+ BUNDLED WITH
136
+ 1.16.2
data/Guardfile ADDED
@@ -0,0 +1,70 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ # Note: The cmd option is now required due to the increasing number of ways
19
+ # rspec may be run, below are examples of the most common uses.
20
+ # * bundler: 'bundle exec rspec'
21
+ # * bundler binstubs: 'bin/rspec'
22
+ # * spring: 'bin/rspec' (This will use spring if running and you have
23
+ # installed the spring binstubs per the docs)
24
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
25
+ # * 'just' rspec: 'rspec'
26
+
27
+ guard :rspec, cmd: 'bundle exec rspec' do
28
+ require 'guard/rspec/dsl'
29
+ dsl = Guard::RSpec::Dsl.new(self)
30
+
31
+ # Feel free to open issues for suggestions and improvements
32
+
33
+ # RSpec files
34
+ rspec = dsl.rspec
35
+ watch(rspec.spec_helper) { rspec.spec_dir }
36
+ watch(rspec.spec_support) { rspec.spec_dir }
37
+ watch(rspec.spec_files)
38
+
39
+ # Ruby files
40
+ ruby = dsl.ruby
41
+ dsl.watch_spec_files_for(ruby.lib_files)
42
+
43
+ # Rails files
44
+ rails = dsl.rails(view_extensions: %w[erb haml slim])
45
+ dsl.watch_spec_files_for(rails.app_files)
46
+ dsl.watch_spec_files_for(rails.views)
47
+
48
+ watch(rails.controllers) do |m|
49
+ [
50
+ rspec.spec.call("routing/#{m[1]}_routing"),
51
+ rspec.spec.call("controllers/#{m[1]}_controller"),
52
+ rspec.spec.call("acceptance/#{m[1]}")
53
+ ]
54
+ end
55
+
56
+ # Rails config changes
57
+ watch(rails.spec_helper) { rspec.spec_dir }
58
+ watch(rails.routes) { "#{rspec.spec_dir}/routing" }
59
+ watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
60
+
61
+ # Capybara features specs
62
+ watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
63
+ watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
64
+
65
+ # Turnip features and steps
66
+ watch(%r{^spec/acceptance/(.+)\.feature$})
67
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
68
+ Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance'
69
+ end
70
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Igor Malinovskiy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # ActionTracker
2
+
3
+ [![Build Status](https://semaphoreci.com/api/v1/ipm/action_tracker/branches/master/badge.svg)](https://semaphoreci.com/ipm/action_tracker)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/cfdcabfc3610c6eac895/maintainability)](https://codeclimate.com/github/psyipm/action_tracker/maintainability)
5
+ [![Gem Version](https://badge.fury.io/rb/action_tracker.svg)](https://badge.fury.io/rb/action_tracker)
6
+
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'action_tracker_client', require: 'action_tracker'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install action_tracker
23
+
24
+ ## Usage
25
+
26
+ ### Configuration
27
+
28
+ You need to put the following values in initializer:
29
+
30
+ ```ruby
31
+ # config/initializers/action_tracker.rb
32
+
33
+ ActionTracker.configure do |config|
34
+ config.api_url = ENV['ACTION_TRACKING_API_URL']
35
+ config.api_key = ENV['ACTION_TRACKING_API_KEY']
36
+ config.api_secret = ENV['ACTION_TRACKING_API_SECRET']
37
+ end
38
+ ```
39
+
40
+ ### Reading records
41
+
42
+ ```ruby
43
+ api = ActionTracker::Models::TransitionRecord.new
44
+ data = api.index(target_id: order.id, target_type: 'Order', per_page: 25, cursor: 'Y3VycmVudF9wYWdl')
45
+
46
+ #<ActionTracker::CollectionProxy:0x00007f958b6970a8
47
+ # ...
48
+
49
+ data.each do |record|
50
+ puts record.inspect
51
+ end
52
+
53
+ #<ActionTracker::Models::TransitionRecord:0x00007f9584d44470 @id="a51b2fe2-fa92-4e91-bdcd-5beee9081903"...
54
+ #<ActionTracker::Models::TransitionRecord:0x00007f9584d3dfd0 @id="810b900d-d24b-4206-85e3-b7a53e55a060"...
55
+ ```
56
+
57
+ ### Writing records
58
+
59
+ Call ActionTracker::Recorder after creating or updating your model:
60
+
61
+ basic usage: `ActionTracker::Recorder.new(:event_name).call(model)`
62
+
63
+ Where :event_name is the name of template. Custom templates could be defined if needed.
64
+
65
+ valid events (templates): `[:create, :update, :destroy]`
66
+
67
+ ```ruby
68
+ # Suppose you have some command, creating an order:
69
+
70
+ class CreateOrderCommand
71
+ # https://github.com/krisleech/wisper
72
+ include Wisper::Publisher
73
+
74
+ def initialize(order_form)
75
+ @form = order_form
76
+
77
+ subscribe(ActionTracker::Recorder.new(:create), on: :ok, with: :call)
78
+ end
79
+
80
+ def call
81
+ if order.save
82
+ broadcast(:ok, order)
83
+ else
84
+ broadcast(:failed, order)
85
+ end
86
+ end
87
+ end
88
+ ```
89
+
90
+ or with ActiveRecord callbacks:
91
+
92
+ ```ruby
93
+ class User < ApplicationRecord
94
+ SKIPPED_ATTRIBUTES = [:password, :updated_at]
95
+
96
+ after_update -> { ActionTracker::Recorder.new(:update, skip: SKIPPED_ATTRIBUTES).call(self) }
97
+ end
98
+ ```
99
+
100
+ ### Defining custom templates
101
+
102
+ Action tracker will look for templates in ActionTracker::Templates module.
103
+
104
+ ```ruby
105
+ # app/models/action_tracker/templates/state_change.rb
106
+
107
+ module ActionTracker
108
+ module Templates
109
+ class StateChange < ActionTracker::Templates::BaseTemplate
110
+
111
+ # Any text, describing the event
112
+ #
113
+ def event_name
114
+ 'State changed'
115
+ end
116
+
117
+ # Can be any string, representing what changed in your model. Max length limited to 1000 chars
118
+ #
119
+ def content
120
+ "from `#{target.previous_state_name}` to `#{target.current_state_name}`"
121
+ end
122
+
123
+ # The user who performed the change. { id: 0, name: 'Anonymous', type: 'System' }
124
+ #
125
+ def user
126
+ @user ||= CurrentUser.fetch
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ module ActionTracker
133
+ module Templates
134
+ module Orders
135
+ class SomeCustomEvent < ActionTracker::Templates::BaseTemplate
136
+ # ...
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ # Call recorder
143
+ ActionTracker::Recorder.new(:state_change).call(target)
144
+ ActionTracker::Recorder.new('Orders::SomeCustomEvent').call(target)
145
+ ```
146
+
147
+ ## Development
148
+
149
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
150
+
151
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
152
+
153
+ ## Contributing
154
+
155
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/action_tracker.
156
+
157
+ ## License
158
+
159
+ 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,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,38 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'action_tracker/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'action_tracker_client'
7
+ spec.version = ActionTracker::VERSION
8
+ spec.authors = ['Igor Malinovskiy']
9
+ spec.email = ['igor.malinovskiy@netfixllc.org']
10
+
11
+ spec.summary = 'ActionTracking service client gem'
12
+ spec.description = 'ActionTracking service integration gem'
13
+ spec.homepage = 'https://github.com/psyipm/action_tracker'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.16'
24
+ spec.add_development_dependency 'guard-rspec', '~> 4.7', '>= 4.7.3'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.0'
27
+ spec.add_development_dependency 'webmock', '~> 3.5', '>= 3.5.1'
28
+
29
+ spec.add_development_dependency 'pry-byebug'
30
+
31
+ spec.add_dependency 'activemodel', '>= 4.0'
32
+ spec.add_dependency 'activesupport', '>= 4.0'
33
+ spec.add_dependency 'api_signature', '~> 0.1.5'
34
+ spec.add_dependency 'httparty', '~> 0.17.0'
35
+ spec.add_dependency 'model_auditor', '~> 0.0.1'
36
+
37
+ spec.add_dependency 'virtus', '~> 1.0', '>= 1.0.5'
38
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'action_tracker'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_tracker/version'
4
+ require 'ostruct'
5
+ require 'model_auditor'
6
+ require 'active_support/core_ext/module'
7
+ require 'active_support/core_ext/object'
8
+
9
+ module ActionTracker
10
+ autoload :Config, 'action_tracker/config'
11
+ autoload :SignedRequest, 'action_tracker/signed_request'
12
+ autoload :Connection, 'action_tracker/connection'
13
+
14
+ autoload :Pagination, 'action_tracker/pagination'
15
+ autoload :CollectionProxy, 'action_tracker/collection_proxy'
16
+
17
+ autoload :Recorder, 'action_tracker/recorder'
18
+
19
+ module Models
20
+ autoload :ApplicationRecord, 'action_tracker/models/application_record'
21
+ autoload :TransitionRecord, 'action_tracker/models/transition_record'
22
+ autoload :Payload, 'action_tracker/models/payload'
23
+ autoload :User, 'action_tracker/models/user'
24
+ end
25
+
26
+ module Templates
27
+ autoload :BaseTemplate, 'action_tracker/templates/base_template'
28
+ autoload :Create, 'action_tracker/templates/create'
29
+ autoload :Update, 'action_tracker/templates/update'
30
+ autoload :Destroy, 'action_tracker/templates/destroy'
31
+ end
32
+
33
+ def self.config
34
+ @config ||= Config.new
35
+ end
36
+
37
+ def self.configure
38
+ yield(config)
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ class CollectionProxy
5
+ include Enumerable
6
+ include ActionTracker::Pagination
7
+
8
+ attr_reader :model_name
9
+
10
+ def initialize(response, model_name)
11
+ @response = response
12
+ @model_name = model_name
13
+ end
14
+
15
+ def code
16
+ @response.code
17
+ end
18
+
19
+ def each
20
+ raw_data.each do |record|
21
+ yield record_klass.new(record)
22
+ end
23
+ end
24
+
25
+ def raw_data
26
+ @response.dig(model_name.plural)
27
+ end
28
+
29
+ def entry_name(options = {})
30
+ model_name.singular.pluralize(options[:count])
31
+ end
32
+
33
+ private
34
+
35
+ def meta
36
+ OpenStruct.new(@response.dig('meta'))
37
+ end
38
+
39
+ def record_klass
40
+ model_name.instance_variable_get(:@klass)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ class ClientNotConfiguredError < StandardError; end
5
+
6
+ class Config
7
+ attr_writer :api_url, :api_key, :api_secret
8
+
9
+ def api_url
10
+ @api_url || raise(ActionTracker::ClientNotConfiguredError, missing_value: :api_url)
11
+ end
12
+
13
+ def api_key
14
+ @api_key || raise(ActionTracker::ClientNotConfiguredError, missing_value: :api_key)
15
+ end
16
+
17
+ def api_secret
18
+ @api_secret || raise(ActionTracker::ClientNotConfiguredError, missing_value: :api_secret)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ class Connection
5
+ [:get, :post, :patch, :put, :delete].each do |type|
6
+ define_method type do |path, options = {}|
7
+ perform_request(type, path, options)
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def perform_request(type, path, options)
14
+ url = File.join(config.api_url, path)
15
+ ActionTracker::SignedRequest.new(type, url, options).perform
16
+ end
17
+
18
+ def config
19
+ @config ||= ActionTracker.config
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'virtus'
4
+ require 'active_model'
5
+
6
+ module ActionTracker
7
+ module Models
8
+ class ApplicationRecord
9
+ include Virtus.model
10
+ include ActiveModel::Validations
11
+
12
+ attribute :id, Integer
13
+
14
+ def self.mimic(model_name)
15
+ @model_name = model_name.to_s.underscore.to_sym
16
+ end
17
+
18
+ def self.mimicked_model_name
19
+ @model_name || infer_model_name
20
+ end
21
+
22
+ def self.infer_model_name
23
+ class_name = name.split('::').last
24
+ return :form if class_name == 'Form'
25
+
26
+ class_name.chomp('Form').underscore.to_sym
27
+ end
28
+
29
+ def self.model_name
30
+ ActiveModel::Name.new(self, nil, mimicked_model_name.to_s.camelize)
31
+ end
32
+
33
+ def model_name
34
+ self.class.model_name
35
+ end
36
+
37
+ def to_key
38
+ [id]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ module Models
5
+ class Payload < ActionTracker::Models::ApplicationRecord
6
+ MAX_CONTENT_LENGTH = 1000
7
+
8
+ attribute :event, String
9
+ attribute :content, String
10
+ attribute :user, ActionTracker::Models::User
11
+
12
+ validates :event, :content, presence: true
13
+
14
+ def with_user(user)
15
+ self[:user] = ActionTracker::Models::User.new(
16
+ id: user.try(:id),
17
+ name: user.try(:name),
18
+ type: user.try(:type)
19
+ )
20
+
21
+ self
22
+ end
23
+
24
+ def for_event(event)
25
+ self[:event] = event.to_s.humanize
26
+ self
27
+ end
28
+
29
+ def with_content(content)
30
+ self[:content] = content.to_s.truncate(MAX_CONTENT_LENGTH)
31
+ self
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ module Models
5
+ class TransitionRecord < ActionTracker::Models::ApplicationRecord
6
+ mimic 'transition'
7
+
8
+ attribute :target_id, Integer
9
+ attribute :target_type, String
10
+ attribute :created_at, DateTime
11
+ attribute :payload, ActionTracker::Models::Payload
12
+
13
+ delegate :event, :content, :user, to: :payload
14
+
15
+ validate :check_payload
16
+
17
+ def with_target(target)
18
+ self.target_id = target.id
19
+ self.target_type = target.class.name
20
+
21
+ self
22
+ end
23
+
24
+ def index(params = {})
25
+ path = [collection_path, params.to_query].reject(&:blank?).compact.join('?')
26
+ @response ||= Connection.new.get(path)
27
+
28
+ ActionTracker::CollectionProxy.new @response, self.class.model_name
29
+ end
30
+
31
+ def collection_path
32
+ 'transitions'
33
+ end
34
+
35
+ def payload
36
+ super.presence || @payload = ActionTracker::Models::Payload.new
37
+ end
38
+
39
+ def attributes
40
+ super.merge(payload: payload.attributes.except(:id))
41
+ end
42
+
43
+ def title
44
+ [event, content].reject(&:blank?).compact.join(': ')
45
+ end
46
+
47
+ private
48
+
49
+ def check_payload
50
+ return if payload.valid?
51
+
52
+ errors.add(:payload, :invalid)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal :true
2
+
3
+ module ActionTracker
4
+ module Models
5
+ class User < ActionTracker::Models::ApplicationRecord
6
+ attribute :id, Integer
7
+ attribute :name, String
8
+ attribute :type, String
9
+
10
+ validates :id, :name, :type, presence: true
11
+
12
+ def self.default_user
13
+ new(id: 0, name: 'Anonymous', type: 'System')
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ module Pagination
5
+ delegate :cursor, to: :meta
6
+
7
+ def size
8
+ raw_data.try(:size)
9
+ end
10
+
11
+ def last_page?
12
+ cursor.blank?
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ class UndefinedTemplateError < StandardError; end
5
+
6
+ # Usage:
7
+ # ActionTracker::Recorder.new(:create).call(order)
8
+ #
9
+ # usage with wisper gem: (https://github.com/krisleech/wisper)
10
+ #
11
+ # MyPublisher.suscribe(ActionTracker::Recorder.new(:update), on: :ok, with: :call)
12
+ #
13
+ class Recorder
14
+ attr_reader :options
15
+
16
+ def initialize(template_name, template_options = {})
17
+ @template_name = template_name
18
+ @options = template_options
19
+ end
20
+
21
+ def call(target)
22
+ form = template_klass.new(target, options).form
23
+ return unless form.valid?
24
+
25
+ @response = connection.post form.collection_path, body: form.attributes
26
+ @response.to_h
27
+ end
28
+
29
+ private
30
+
31
+ def template_name
32
+ @template_name.to_s.classify
33
+ end
34
+
35
+ def template_klass
36
+ klass = "ActionTracker::Templates::#{template_name}".safe_constantize
37
+ raise UndefinedTemplateError, template_name unless klass
38
+
39
+ klass
40
+ end
41
+
42
+ def connection
43
+ @connection ||= ActionTracker::Connection.new
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api_signature'
4
+ require 'httparty'
5
+
6
+ module ActionTracker
7
+ class SignedRequest
8
+ include ::HTTParty
9
+
10
+ attr_reader :request_method, :url, :options
11
+
12
+ delegate :config, to: :ActionTracker
13
+
14
+ def initialize(request_method, url, options = {})
15
+ @request_method = request_method.to_s.downcase
16
+ @url = URI.parse(url)
17
+ @options = options
18
+ end
19
+
20
+ def perform
21
+ self.class.send(request_method, url, headers: headers, body: body, format: :json)
22
+ end
23
+
24
+ private
25
+
26
+ def headers
27
+ (options[:headers] || {}).merge(signature_headers)
28
+ end
29
+
30
+ def body
31
+ options[:body]
32
+ end
33
+
34
+ def signature_headers
35
+ ApiSignature::Builder.new(signature_options).headers
36
+ end
37
+
38
+ def signature_options
39
+ {
40
+ access_key: config.api_key,
41
+ secret: config.api_secret,
42
+ request_method: request_method,
43
+ path: url.path
44
+ }
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ module Templates
5
+ class BaseTemplate
6
+ attr_reader :target, :options
7
+
8
+ def initialize(target, options = {})
9
+ @target = target
10
+ @options = options
11
+ end
12
+
13
+ def form
14
+ @form ||= ActionTracker::Models::TransitionRecord.new(payload: payload).with_target(target)
15
+ end
16
+
17
+ protected
18
+
19
+ def payload
20
+ @payload ||= build_payload
21
+ end
22
+
23
+ def build_payload
24
+ payload_instance = ActionTracker::Models::Payload.new
25
+ payload_instance.with_user(user).with_content(content).for_event(event_title)
26
+ end
27
+
28
+ def user
29
+ @user ||= options[:user] || ActionTracker::Models::User.default_user
30
+ end
31
+
32
+ # Override
33
+ #
34
+ def content
35
+ options[:content]
36
+ end
37
+
38
+ def event_title
39
+ [title_prepend, event_name, title_append].compact.join(' ').humanize
40
+ end
41
+
42
+ def event_name
43
+ # Override
44
+ end
45
+
46
+ def title_prepend
47
+ options[:title_prepend]
48
+ end
49
+
50
+ def title_append
51
+ options[:title_append]
52
+ end
53
+
54
+ def reference
55
+ options[:reference]
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ module Templates
5
+ class Create < BaseTemplate
6
+ def event_name
7
+ 'Created'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ module Templates
5
+ class Destroy < BaseTemplate
6
+ def event_name
7
+ 'Deleted'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionTracker
4
+ module Templates
5
+ class Update < BaseTemplate
6
+ def event_name
7
+ 'Updated'
8
+ end
9
+
10
+ def content
11
+ @content ||= ModelAuditor::Changes.new(inspectable, changes).filter(skipped_attributes).audit
12
+ end
13
+
14
+ private
15
+
16
+ def inspectable
17
+ options[:reference] || target
18
+ end
19
+
20
+ def changes
21
+ options[:changes]
22
+ end
23
+
24
+ def skipped_attributes
25
+ options[:skip]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module ActionTracker
2
+ VERSION = '0.0.1'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,260 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: action_tracker_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Igor Malinovskiy
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-07-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: guard-rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.7'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 4.7.3
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '4.7'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 4.7.3
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '10.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '10.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: webmock
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.5'
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 3.5.1
85
+ type: :development
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '3.5'
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 3.5.1
95
+ - !ruby/object:Gem::Dependency
96
+ name: pry-byebug
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ - !ruby/object:Gem::Dependency
110
+ name: activemodel
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '4.0'
116
+ type: :runtime
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '4.0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: activesupport
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '4.0'
130
+ type: :runtime
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '4.0'
137
+ - !ruby/object:Gem::Dependency
138
+ name: api_signature
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: 0.1.5
144
+ type: :runtime
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: 0.1.5
151
+ - !ruby/object:Gem::Dependency
152
+ name: httparty
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: 0.17.0
158
+ type: :runtime
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: 0.17.0
165
+ - !ruby/object:Gem::Dependency
166
+ name: model_auditor
167
+ requirement: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - "~>"
170
+ - !ruby/object:Gem::Version
171
+ version: 0.0.1
172
+ type: :runtime
173
+ prerelease: false
174
+ version_requirements: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - "~>"
177
+ - !ruby/object:Gem::Version
178
+ version: 0.0.1
179
+ - !ruby/object:Gem::Dependency
180
+ name: virtus
181
+ requirement: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - "~>"
184
+ - !ruby/object:Gem::Version
185
+ version: '1.0'
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: 1.0.5
189
+ type: :runtime
190
+ prerelease: false
191
+ version_requirements: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - "~>"
194
+ - !ruby/object:Gem::Version
195
+ version: '1.0'
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: 1.0.5
199
+ description: ActionTracking service integration gem
200
+ email:
201
+ - igor.malinovskiy@netfixllc.org
202
+ executables: []
203
+ extensions: []
204
+ extra_rdoc_files: []
205
+ files:
206
+ - ".gitignore"
207
+ - ".rspec"
208
+ - ".rubocop.yml"
209
+ - ".ruby-version"
210
+ - ".travis.yml"
211
+ - Gemfile
212
+ - Gemfile.lock
213
+ - Guardfile
214
+ - LICENSE.txt
215
+ - README.md
216
+ - Rakefile
217
+ - action_tracker.gemspec
218
+ - bin/console
219
+ - bin/setup
220
+ - lib/action_tracker.rb
221
+ - lib/action_tracker/collection_proxy.rb
222
+ - lib/action_tracker/config.rb
223
+ - lib/action_tracker/connection.rb
224
+ - lib/action_tracker/models/application_record.rb
225
+ - lib/action_tracker/models/payload.rb
226
+ - lib/action_tracker/models/transition_record.rb
227
+ - lib/action_tracker/models/user.rb
228
+ - lib/action_tracker/pagination.rb
229
+ - lib/action_tracker/recorder.rb
230
+ - lib/action_tracker/signed_request.rb
231
+ - lib/action_tracker/templates/base_template.rb
232
+ - lib/action_tracker/templates/create.rb
233
+ - lib/action_tracker/templates/destroy.rb
234
+ - lib/action_tracker/templates/update.rb
235
+ - lib/action_tracker/version.rb
236
+ homepage: https://github.com/psyipm/action_tracker
237
+ licenses:
238
+ - MIT
239
+ metadata: {}
240
+ post_install_message:
241
+ rdoc_options: []
242
+ require_paths:
243
+ - lib
244
+ required_ruby_version: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - ">="
247
+ - !ruby/object:Gem::Version
248
+ version: '0'
249
+ required_rubygems_version: !ruby/object:Gem::Requirement
250
+ requirements:
251
+ - - ">="
252
+ - !ruby/object:Gem::Version
253
+ version: '0'
254
+ requirements: []
255
+ rubyforge_project:
256
+ rubygems_version: 2.7.6.2
257
+ signing_key:
258
+ specification_version: 4
259
+ summary: ActionTracking service client gem
260
+ test_files: []