bidding 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5d5697d795874fbac30fd7513e22eccaed0ff1dc
4
+ data.tar.gz: 6549800b7a60c5d993c263d84570ecafecfbb0e9
5
+ SHA512:
6
+ metadata.gz: ed39d7e778f30cd4015923f8e8c20b39d2b4e6c13acca76a24fec18c42ae3d15557fa0055da96e668c70c209471c6827d81108be5a3dec1ae60cb92b06c8588a
7
+ data.tar.gz: e78b7fa2c025e9c80cc5d7fc1a6fc22bcc934ca6f91234d6262150a568bed697454035e0c647ec757d4ea8e6ab20fc7b070767a47d3f15c7bb45c78c18c39783
data/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # Bidding
2
+
3
+ Bidding is a small gem for handling the command pattern.
4
+ The goal is to let the commands be easy to implement, and to provide basic infrastructure for executing commands.
5
+
6
+ The infrastructure is a command queue, a command executor which executes transactions and a transaction log which keeps a record of all executed commands.
7
+
8
+ It's inspried from the CQRS movement, except it is left up to the implementor to decide if the commands should issue events and thus use eventsourcing.
9
+ Or if it simple should keep a record of all executed commands.
10
+
11
+ The gem uses an in memory transaction log and queue. Right now the idea is that adapters to different queues and storage backed logs should be implemented outside the gem.
12
+ Thus keeping the gem dependencies as small as possible.
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ gem 'bidding'
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install bidding
27
+
28
+ ## Usage
29
+
30
+ When you require 'bidding' you get access to a bunch of classes.
31
+ The most important one is the Command class.
32
+
33
+ ### Commands
34
+ To create a command simple inherit from the Command class and add an execute method.
35
+
36
+ ```ruby
37
+
38
+ class Nop < Command
39
+
40
+ def execute
41
+ # this code executes what the command does
42
+ end
43
+
44
+ end
45
+
46
+ ```
47
+
48
+ This will allow you to execute the nop command simply by parsing and executing the command.
49
+
50
+
51
+ ```ruby
52
+
53
+ Command.parse("nop").execute()
54
+
55
+ ```
56
+
57
+ #### Parameters
58
+
59
+ A command wouldn't be much use unless it could take a few paramters. Since the focus here is to have the command code as readable as possible.
60
+ You simple specify the arguments of the command in order in which they appear.
61
+
62
+ ```ruby
63
+
64
+ class Nop < Command
65
+
66
+ parameters :type, :id, :title
67
+
68
+ def execute
69
+ # this code executes what the command does
70
+ # you can use type, id and title here.
71
+ end
72
+
73
+ end
74
+
75
+ ```
76
+
77
+ the parameters method adds properties to the command which correspond to the location of the arguments in the commandline string.
78
+ The code above has 3 parameters: type, id and title. These are all available in the execute method.
79
+
80
+ Example:
81
+
82
+ ```ruby
83
+
84
+ class Nop < Command
85
+
86
+ parameters :type, :id, :title
87
+
88
+ def execute
89
+ p type
90
+ p id
91
+ p URI.unescape(title)
92
+ end
93
+
94
+ end
95
+
96
+ Command.parse("nop the_type 12345 awesome%20stuff!").execute()
97
+
98
+ # outputs
99
+ # > the_type
100
+ # > 12345
101
+ # > awesome!
102
+
103
+ ```
104
+
105
+ Each parameter is separated with a space. If you would like to add parameter values that contain spaces I recommend URI encoding them first.
106
+ This allows you to handle strings that might contain spaces.
107
+
108
+ ### Executor
109
+
110
+ The commands executor simply takes a hash of commands from a queue and executes them in order. Successfull commands are pushed to a log.
111
+ If the commands are not successfull the commands are left on the queue.
112
+
113
+ The gem contains an inmemory queue and log for testing purposes.
114
+
115
+ ## Contributing
116
+
117
+ 1. Fork it ( http://github.com/morkeleb/bidding/fork )
118
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
119
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
120
+ 4. Push to the branch (`git push origin my-new-feature`)
121
+ 5. Create new Pull Request
@@ -0,0 +1,36 @@
1
+ class Command
2
+ attr_accessor :name, :arguments, :user, :commandline
3
+
4
+ def self.parameters(*args)
5
+ args.each do |arg|
6
+ self.class_eval("def #{arg};arguments["+args.index(arg).to_s+"];end")
7
+ end
8
+ end
9
+
10
+ def self.parse(commandline, user)
11
+ parts = commandline.split(" ")
12
+ name = camel_case parts.shift
13
+ command = Kernel.const_get(name).new
14
+ command.name = name
15
+ command.user = user
16
+ command.arguments = parts
17
+ command.commandline = commandline
18
+ return command
19
+ end
20
+
21
+ def self.camel_case(s)
22
+ return s if s !~ /_/ && s =~ /[A-Z]+.*/
23
+ s.split('_').map{|e| e.capitalize}.join
24
+ end
25
+
26
+
27
+ def execute
28
+ raise 'execute method not implemented for class: ' + self.class.name
29
+ end
30
+
31
+ def replay
32
+ @replay = true
33
+ execute
34
+ end
35
+
36
+ end
@@ -0,0 +1,37 @@
1
+ require 'observer'
2
+
3
+ class CommandQueue
4
+ include Observable
5
+
6
+ def initialize
7
+ @list = []
8
+ end
9
+
10
+ def list
11
+ @list
12
+ end
13
+
14
+ def pushCommands(commands)
15
+ @list.push commands
16
+ changed
17
+ notify_observers commands
18
+
19
+ end
20
+
21
+ def length
22
+ @list.length
23
+ end
24
+
25
+ def return(transaction)
26
+ @list.insert(0, transaction)
27
+ end
28
+
29
+ def next
30
+ @list.shift
31
+ end
32
+
33
+ def exists? (id)
34
+ @list.index { |item| item[:id] == id} != nil
35
+ end
36
+
37
+ end
@@ -0,0 +1,7 @@
1
+
2
+ class Nop < Command
3
+
4
+ def execute
5
+ end
6
+
7
+ end
@@ -0,0 +1,23 @@
1
+ class CommandsExecutor
2
+ def initialize(queue, transactionLog)
3
+ @queue = queue
4
+ @transactionLog = transactionLog
5
+ @queue.add_observer(self)
6
+ end
7
+
8
+ def update(commands)
9
+ transaction = @queue.next
10
+ transaction["commands"].each {|c|
11
+ co = Command.parse(c, transaction["user"])
12
+ co.execute()
13
+ }
14
+ @transactionLog.push transaction
15
+ rescue Exception
16
+ p 'EXCEPTION'
17
+ p $!.to_s
18
+ p $!.backtrace
19
+ @queue.return transaction
20
+ end
21
+
22
+
23
+ end
@@ -0,0 +1,30 @@
1
+ class InMemoryTransactionLog
2
+
3
+ def initialize
4
+ @list = []
5
+ end
6
+
7
+ def push(transaction)
8
+ @list.push(transaction)
9
+ end
10
+
11
+ def from(date)
12
+ @list.select { |entry| entry["date"] < date}
13
+ end
14
+
15
+
16
+ def containsId? (id)
17
+ @list.index { |trans| trans["id"] == id } != nil
18
+ end
19
+
20
+ def delete(entry)
21
+ @list.delete_if { |entry| entry["id"] == entry}
22
+ end
23
+
24
+ # for testing purposes only
25
+ def log
26
+ @list
27
+ end
28
+
29
+
30
+ end
@@ -0,0 +1,37 @@
1
+ require 'json'
2
+ require 'date'
3
+
4
+ class Exporter
5
+
6
+ def initialize(log)
7
+ @log = log
8
+ end
9
+
10
+ def export_from(date)
11
+ entries = (@log.from date.to_f).to_a
12
+ write entries, to(file_by date)
13
+
14
+ delete entries
15
+ end
16
+
17
+ def write(entries, path)
18
+ File.open(path, "w") { |file|
19
+ file.write JSON entries
20
+ }
21
+ end
22
+
23
+ def delete(entries)
24
+ entries.each do |entry|
25
+ @log.delete entry
26
+ end
27
+ end
28
+
29
+ def to(file)
30
+ file
31
+ end
32
+
33
+ def file_by(date)
34
+ return "./tmp/log-" + Time.now.utc.to_date.to_s + ".json"
35
+ end
36
+
37
+ end
@@ -0,0 +1,22 @@
1
+
2
+ Dir["./lib/commands/**/*.rb"].sort.each {|f| require f}
3
+ Dir["./lib/models/**/*.rb"].sort.each {|f| require f}
4
+ class Importer
5
+
6
+ def import file
7
+ p 'importing file ' + file
8
+ transaction = JSON File.read file
9
+
10
+ transaction.each { |trans|
11
+ commands = trans["commands"]
12
+ commands.each { |command_string|
13
+ command = Command.parse command_string, trans["user"]
14
+ command.replay
15
+ }
16
+ }
17
+
18
+ File.delete file
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,18 @@
1
+ require 'date'
2
+
3
+ class TransactionLog
4
+
5
+
6
+ def adapter=(adapter)
7
+ @adapter = adapter
8
+ end
9
+
10
+ def adapter
11
+ @adapter ||= InMemoryTransactionLog.new
12
+ end
13
+
14
+ def push(transaction)
15
+ transaction["date"] = Time.now.utc.to_f
16
+ adapter.push(transaction)
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Bidding
2
+ VERSION = "0.0.1"
3
+ end
data/lib/bidding.rb ADDED
@@ -0,0 +1,14 @@
1
+ require "bidding/version"
2
+ require 'bidding/command'
3
+ require 'bidding/command_queue'
4
+ require 'bidding/commands_executor'
5
+ require 'bidding/transaction_log'
6
+ require 'bidding/in_memory_transaction_log'
7
+ require 'bidding/commands/nop'
8
+ require 'bidding/tools/exporter'
9
+ require 'bidding/tools/importer'
10
+
11
+ module Bidding
12
+ # Your code goes here...
13
+
14
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bidding
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Morten Nielsen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-18 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.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '2.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '2.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Lets you create commands that will parse from oneline strings, and execute
56
+ them.
57
+ email: morten@morkeleb.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - README.md
63
+ - lib/bidding.rb
64
+ - lib/bidding/command.rb
65
+ - lib/bidding/command_queue.rb
66
+ - lib/bidding/commands/nop.rb
67
+ - lib/bidding/commands_executor.rb
68
+ - lib/bidding/in_memory_transaction_log.rb
69
+ - lib/bidding/tools/exporter.rb
70
+ - lib/bidding/tools/importer.rb
71
+ - lib/bidding/transaction_log.rb
72
+ - lib/bidding/version.rb
73
+ homepage: https://github.com/morkeleb/bidding
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.2.2
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: A small library for handling commands
97
+ test_files: []