bodhi-slam 0.7.3 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1fbe5ba6be9aceacf0acdc118e78a1a95ddd32ee
4
- data.tar.gz: a7eae1661963319627d4694dfc90dd8bc5b1e327
3
+ metadata.gz: 7a728bbc653fe51f2aa54fd7fcc07874205616c8
4
+ data.tar.gz: ecd5e410504d479843dfcc4ebe2823815161c6fe
5
5
  SHA512:
6
- metadata.gz: 938995fe271d5b2085a80c153931e3d0378c05daf587e247ef7baad00c2f48d4741b59299842c4042c0bbb3cf56ab44445983c200fbd6936e445d6ba3fb07dd1
7
- data.tar.gz: 8f72e7317218ece3d876e7b803f738e55176e2de400bc368fa1c4d50bb1700c40f58be5d219fa1c2841d63a00a5d8a3cf18b0d1d393c64b30ae96213ef30ff75
6
+ metadata.gz: 3ca39c510350b552edbd793a8b0c42700f0c3125ca103f613fd8ee1a8c618076e2063f9630a739b7e67a99c05378f0b0d9b2a05249c2c3162fbba64c60d3d3e4
7
+ data.tar.gz: 32749263d7225845eb31e8780a02b446c19ad2a559fcc295bc01767157f1f15a958f90fd32bb904346f4595bad27e3262794b696287e631ed734e1c5c2482fab
@@ -80,8 +80,13 @@ module Bodhi
80
80
  # s.update_attributes(bar: 10)
81
81
  # s.attributes # => { foo: "test", bar: 10 }
82
82
  def update_attributes(params)
83
- params.each do |param_key, param_value|
84
- send("#{param_key}=", param_value)
83
+ params.each do |property, value|
84
+ property_options = self.class.properties[property.to_sym]
85
+ if property_options.nil?
86
+ send("#{property}=", value)
87
+ else
88
+ send("#{property}=", Bodhi::Support.coerce(value, property_options))
89
+ end
85
90
  end
86
91
  end
87
92
 
@@ -0,0 +1,103 @@
1
+ module Bodhi
2
+ module Simulation
3
+ class NormalDistributionCurve
4
+ include Bodhi::Properties
5
+ include Bodhi::Validations
6
+
7
+ property :mean, type: Float
8
+ property :mean_range, type: Float, multi: true
9
+
10
+ property :std_dev, type: Float
11
+ property :std_dev_range, type: Float, multi: true
12
+
13
+ property :scale, type: Float
14
+ property :title, type: String
15
+
16
+ validates :mean, required: true
17
+ validates :std_dev, required: true, min: 0.0001
18
+ validates :scale, required: true, min: 0.0, max: 1.0
19
+ end
20
+
21
+ class NormalDistribution
22
+ include Bodhi::Properties
23
+ include Bodhi::Validations
24
+
25
+ GAUSSIAN_FUNCTION = lambda{|x,u,o,s| 1/Math.sqrt(2*Math::PI*(o**2)) * Math.exp(-((x-u)**2)/(2*(o**2))) * s }
26
+
27
+ property :curves, type: Bodhi::Simulation::NormalDistributionCurve, multi: true
28
+ validates :curves, required: true
29
+
30
+ # ================
31
+ # Class Methods
32
+ # ================
33
+
34
+ # Accepts an array of Bodhi::Simulation::NormalDistribution::Curves objects
35
+ # Randomizes the +mean+ and +std_dev+ properties based on the given
36
+ # +mean_range+ and +std_dev_range+ properties
37
+ # Returns a new Bodhi::Simulation::NormalDistribution object with the randomized properties
38
+ #
39
+ # curves = [{mean_range: [45.4, 50.0], std_dev_range: [1.2, 2.2], scale: 0.2, title: "Dinner rush"}]
40
+ #
41
+ # Bodhi::Simulation::NormalDistribution.randomize(curves)
42
+ # #=> #<Bodhi::Simulation::NormalDistribution @mean=47.1234 @std_dev=1.7543 @mean_range=[45.4, 50.0] @std_dev_range=[1.2, 2.2] @scale=0.2 @title="Dinner rush">
43
+ #
44
+ def self.randomize(curves)
45
+ unless curves.is_a?(Array)
46
+ raise ArgumentError.new("+curves+ must be an Array")
47
+ end
48
+
49
+ randomized_curves = curves.collect do |curve|
50
+ # Coerce the given +curve+ into a Bodhi::Simulation::NormalDistributionCurve
51
+ unless curve.is_a?(Bodhi::Simulation::NormalDistributionCurve)
52
+ begin
53
+ curve = Bodhi::Simulation::NormalDistributionCurve.new(curve)
54
+ rescue Exception => e
55
+ raise ArgumentError.new("The value: #{curve} is not a valid Bodhi::Simulation::NormalDistributionCurve object. Error: #{e}")
56
+ end
57
+ end
58
+
59
+ # Check if the user supplied a range for mean and std_dev values
60
+ # These are optional properies, but a required for randomizing a curve
61
+ if curve.mean_range.nil? || curve.std_dev_range.nil?
62
+ raise ArgumentError.new("Unable to randomize the curve: #{curve.to_json}. Reason: missing mandatory +mean_range+ OR +std_dev_range+ properties.")
63
+ end
64
+
65
+ # Calculate a random mean and standard deviation
66
+ random_mean = rand(curve.mean_range[0]..curve.mean_range[1])
67
+ random_std_dev = rand(curve.std_dev_range[0]..curve.std_dev_range[1])
68
+
69
+ # Create a new randomized curve object
70
+ randomized_curve = curve.clone
71
+ randomized_curve.mean = random_mean
72
+ randomized_curve.std_dev = random_std_dev
73
+
74
+ # Check if the randomized curve is valid
75
+ if randomized_curve.invalid?
76
+ raise ArgumentError.new("Invalid Bodhi::Simulation::NormalDistributionCurve. Reasons: #{randomized_curve.errors.to_a}")
77
+ end
78
+
79
+ # return the randomized curve
80
+ randomized_curve
81
+ end
82
+
83
+ # Return a new NormalDistribution with the randomized curves
84
+ Bodhi::Simulation::NormalDistribution.new(curves: randomized_curves)
85
+ end
86
+
87
+ # ================
88
+ # Instance Methods
89
+ # ================
90
+
91
+ # Evaluates the value of +y+ at position +x+
92
+ # Raises ArgumentError if +x+ is not a Float or Integer
93
+ def calculate(x)
94
+ unless x.is_a?(Integer) || x.is_a?(Float)
95
+ raise ArgumentError.new("Expected Integer or Float but recieved: #{x.class}")
96
+ end
97
+
98
+ curves.collect{ |curve| GAUSSIAN_FUNCTION.call(x, curve.mean, curve.std_dev, curve.scale) }.reduce(:+)
99
+ end
100
+
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,94 @@
1
+ module Bodhi
2
+ class SimulationFrame
3
+ include Bodhi::Properties
4
+ include Bodhi::Validations
5
+
6
+ property :iteration, type: Integer, default: 0
7
+ property :time, type: DateTime
8
+ end
9
+
10
+ class Simulator
11
+ include Bodhi::Properties
12
+ include Bodhi::Validations
13
+
14
+ # Initial conditions
15
+ property :starts_at, type: DateTime
16
+ property :iterations, type: Integer
17
+ property :time_units, type: String
18
+ property :time_scale, type: Integer, default: 1
19
+
20
+ # Dynamic attributes
21
+ # Updated every iteration
22
+ property :current_frame, type: SimulationFrame
23
+
24
+ # Model validations
25
+ validates :starts_at, required: true
26
+ validates :iterations, required: true, min: 1
27
+ validates :time_units, required: true
28
+ validates :time_scale, min: 1
29
+
30
+ # ================
31
+ # Class Methods
32
+ # ================
33
+
34
+ # Run a new simulation using the given +settings+ and +&block+
35
+ # Yields the +simulation.current_time+ to the user defined +&block+
36
+ # Ignores +current_frame+ settings and zero's them before the simulation
37
+ #
38
+ # Bodhi::Simulator.execute(starts_at: "2016-05-10", iterations: 10, time_units: "days") do |current_time|
39
+ # # do some simulation stuff!
40
+ # end
41
+ #
42
+ # # You can even go crazy and do things like:
43
+ # Bodhi::Simulator.execute(...) do |outer_time|
44
+ # # do some simulation stuff!
45
+ # Bodhi::Simulator.execute(...) do |inner_time|
46
+ # # do a nested simulation!
47
+ # end
48
+ # # do more stuff after the netsted simulation...
49
+ # end
50
+ def self.execute(settings, &block)
51
+ simulation = Bodhi::Simulator.new(settings)
52
+
53
+ if simulation.invalid?
54
+ raise ArgumentError.new("Invalid settings: #{simulation.errors.to_a}")
55
+ end
56
+
57
+ if simulation.current_frame.nil?
58
+ simulation.current_frame = Bodhi::SimulationFrame.new(iteration: 0, time: simulation.starts_at)
59
+ else
60
+ simulation.current_frame.iteration = 0
61
+ simulation.current_frame.time = simulation.starts_at
62
+ end
63
+
64
+ until simulation.complete?
65
+ yield simulation.current_frame.clone
66
+ simulation.increment
67
+ end
68
+ end
69
+
70
+ # ================
71
+ # Instance Methods
72
+ # ================
73
+
74
+ # returns true if the simulation has processed all iterations
75
+ def complete?
76
+ self.current_frame.iteration == self.iterations
77
+ end
78
+
79
+ # Increments the simulation loop by one iteration
80
+ # Updates +current_time+ to the new time offset
81
+ # Updates +current_iteration+ to the next iteration
82
+ #
83
+ # simulation = Bodhi::Simulator.new(starts_at: "2016-05-13", iterations: 2, time_units: "days")
84
+ # simulation.increment
85
+ # simulation #=> #<Bodhi::Simulator @starts_at: "2016-05-13" @iterations: 2, @time_units: "days", @current_time: "2016-05-14", @current_iteration: 1 >
86
+ #
87
+ def increment
88
+ self.current_frame.iteration += 1
89
+ self.current_frame.time = self.starts_at + (self.current_frame.iteration * 1.send(self.time_units) * self.time_scale)
90
+ end
91
+ end
92
+ end
93
+
94
+ Dir[File.dirname(__FILE__) + "/simulation/*.rb"].each { |file| require file }
@@ -148,13 +148,13 @@ module Bodhi
148
148
  raise ArgumentError.new("Expected #{type.class} to be a Bodhi::Type")
149
149
  end
150
150
 
151
- klass = Object.const_set(type.name, Class.new { include Bodhi::Resource })
151
+ klass = Object.const_set(type.name, Class.new { include Bodhi::Resource, ActiveModel::Model })
152
152
 
153
153
  type.properties.each_pair do |attr_name, attr_properties|
154
154
  attr_properties = Bodhi::Support.symbolize_keys(attr_properties)
155
155
 
156
156
  # remove Sanitizers
157
- attr_properties.delete_if{ |key, value| [:trim, :unique, :default, :isCurrentUser, :toLower].include?(key) }
157
+ attr_properties.delete_if{ |key, value| [:trim, :unique, :default, :isCurrentUser, :toLower, :encrypt].include?(key) }
158
158
 
159
159
  # Do not add factories or validations for system properties
160
160
  unless attr_properties[:system] == true
data/lib/bodhi-slam.rb CHANGED
@@ -7,6 +7,11 @@ require 'faraday-http-cache'
7
7
  require 'net/http/persistent'
8
8
  require 'regexp-examples'
9
9
 
10
+ # Active Support
11
+ require 'active_model'
12
+ require 'active_support'
13
+ require 'active_support/core_ext'
14
+
10
15
  require 'bodhi-slam/support'
11
16
  require 'bodhi-slam/validations'
12
17
  require 'bodhi-slam/errors'
@@ -25,6 +30,9 @@ require 'bodhi-slam/users'
25
30
  require 'bodhi-slam/profiles'
26
31
  require 'bodhi-slam/queries'
27
32
 
33
+ # Discrete Event Simulator
34
+ require 'bodhi-slam/simulator'
35
+
28
36
  class BodhiSlam
29
37
  # Defines a context to interact with the Bodhi API
30
38
  # Including a +server+, +namespace+, +username+, +password+ or +cookie+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bodhi-slam
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - willdavis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-14 00:00:00.000000000 Z
11
+ date: 2016-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '2.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activemodel
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.2'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: json
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -171,6 +185,8 @@ files:
171
185
  - lib/bodhi-slam/properties.rb
172
186
  - lib/bodhi-slam/queries.rb
173
187
  - lib/bodhi-slam/resource.rb
188
+ - lib/bodhi-slam/simulation/normal_distribution.rb
189
+ - lib/bodhi-slam/simulator.rb
174
190
  - lib/bodhi-slam/support.rb
175
191
  - lib/bodhi-slam/types.rb
176
192
  - lib/bodhi-slam/types/index.rb
@@ -209,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
225
  version: '0'
210
226
  requirements: []
211
227
  rubyforge_project:
212
- rubygems_version: 2.4.5
228
+ rubygems_version: 2.5.1
213
229
  signing_key:
214
230
  specification_version: 4
215
231
  summary: Ruby bindings for the Bodhi API & factories for random data generation