thermometer 0.0.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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +32 -0
  5. data/lib/generators/thermometer/install_generator.rb +14 -0
  6. data/lib/generators/thermometer/templates/thermometer.yml +76 -0
  7. data/lib/tasks/thermometer_tasks.rake +4 -0
  8. data/lib/thermometer.rb +31 -0
  9. data/lib/thermometer/active_record.rb +50 -0
  10. data/lib/thermometer/configuration.rb +116 -0
  11. data/lib/thermometer/evaluate.rb +87 -0
  12. data/lib/thermometer/temperature.rb +90 -0
  13. data/lib/thermometer/version.rb +3 -0
  14. data/test/dummy/README.rdoc +28 -0
  15. data/test/dummy/Rakefile +6 -0
  16. data/test/dummy/app/assets/javascripts/application.js +13 -0
  17. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  18. data/test/dummy/app/controllers/application_controller.rb +5 -0
  19. data/test/dummy/app/helpers/application_helper.rb +2 -0
  20. data/test/dummy/app/models/User.rb +28 -0
  21. data/test/dummy/app/models/message.rb +3 -0
  22. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  23. data/test/dummy/bin/bundle +3 -0
  24. data/test/dummy/bin/rails +4 -0
  25. data/test/dummy/bin/rake +4 -0
  26. data/test/dummy/config.ru +4 -0
  27. data/test/dummy/config/application.rb +25 -0
  28. data/test/dummy/config/boot.rb +5 -0
  29. data/test/dummy/config/database.yml +25 -0
  30. data/test/dummy/config/environment.rb +5 -0
  31. data/test/dummy/config/environments/development.rb +29 -0
  32. data/test/dummy/config/environments/production.rb +80 -0
  33. data/test/dummy/config/environments/test.rb +36 -0
  34. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  35. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  36. data/test/dummy/config/initializers/inflections.rb +16 -0
  37. data/test/dummy/config/initializers/mime_types.rb +5 -0
  38. data/test/dummy/config/initializers/secret_token.rb +12 -0
  39. data/test/dummy/config/initializers/session_store.rb +3 -0
  40. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  41. data/test/dummy/config/locales/en.yml +23 -0
  42. data/test/dummy/config/routes.rb +56 -0
  43. data/test/dummy/config/thermometer.yml +60 -0
  44. data/test/dummy/db/development.sqlite3 +0 -0
  45. data/test/dummy/db/migrate/20140213183243_create_users.rb +8 -0
  46. data/test/dummy/db/migrate/20140214001211_create_messages.rb +9 -0
  47. data/test/dummy/db/schema.rb +31 -0
  48. data/test/dummy/db/seeds.rb +13 -0
  49. data/test/dummy/db/test.sqlite3 +0 -0
  50. data/test/dummy/lib/helper_methods.rb +4 -0
  51. data/test/dummy/log/development.log +10949 -0
  52. data/test/dummy/log/test.log +26820 -0
  53. data/test/dummy/public/404.html +58 -0
  54. data/test/dummy/public/422.html +58 -0
  55. data/test/dummy/public/500.html +57 -0
  56. data/test/dummy/public/favicon.ico +0 -0
  57. data/test/dummy/test/fixtures/messages.yml +29 -0
  58. data/test/dummy/test/fixtures/users.yml +12 -0
  59. data/test/dummy/test/models/user_test.rb +175 -0
  60. data/test/dummy/test/test_helper.rb +20 -0
  61. data/test/test_helper.rb +30 -0
  62. data/test/thermometer_test.rb +8 -0
  63. metadata +183 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 24f4d884925af863eb840198fb0ab404db19cc0c
4
+ data.tar.gz: 47415f7a12bfcf6e07d39b09d806c879f23fb269
5
+ SHA512:
6
+ metadata.gz: 523a542fdd016ca8218446c006edb19429fdb5130ee9dfd8d6842d3c2633c499f5b9b26264baaa97dc606f3a416d53e0f00a455d01afa8391950b8d224a1cdfc
7
+ data.tar.gz: a1d7ebed8a309fcc8a5723a51bd5bf378f5640afcec26722f69acfea22066aa80cd26e4785e9eb1299aecfe9a01e1564353859e6381f07d187df73a67eb0ace2
@@ -0,0 +1,20 @@
1
+ Copyright 2014 NICK NEWELL
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.
@@ -0,0 +1,3 @@
1
+ = Thermometer
2
+
3
+ This project rocks and uses MIT-LICENSE.
@@ -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 = 'Thermometer'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = false
29
+ end
30
+
31
+
32
+ task default: :test
@@ -0,0 +1,14 @@
1
+ module Thermometer
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ desc "Copy default YML to config"
8
+ def copy_yml
9
+ copy_file 'thermometer.yml', 'config/thermometer.yml'
10
+ git add: "config/thermometer.yml"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,76 @@
1
+ # Here are the settings that are common to all environments
2
+ common: &default_settings
3
+
4
+ # ============================== DEFAULT OPTIONS ===============================
5
+
6
+ date: updated_at
7
+ sample: 1
8
+ order: DESC
9
+
10
+
11
+ # ============================== TIME RANGES ===============================
12
+
13
+ # By default this time range wil be used, unless explicitly defined as an option in the class
14
+ # method 'measure_heat_on'
15
+ # Example :
16
+ # measure_the_heat_on :friends , time_range: :brief
17
+ # measure_the_heat_on :friends , time_range: {hot: 1.second..1.hour, cold: 1.hour..1.year}
18
+ #
19
+ #
20
+
21
+ time_range: detailed
22
+ intensity_range: brief
23
+
24
+ # Multiple time ranges can be defined here.
25
+
26
+ time:
27
+ detailed:
28
+ freezing: 4.months..100.years
29
+ frigid: 3.months..4.months
30
+ frosty: 2.months..3.months
31
+ cold: 5.weeks..2.months
32
+ chilly: 3.weeks..5.weeks
33
+ cool: 2.weeks..3.weeks
34
+ temperate: 1.week..2.weeks
35
+ tepid: 4.days..1.week
36
+ lukewarm: 2.days..4.days
37
+ warm: 1.day..2.days
38
+ hot: 14.hours..1.day
39
+ burning: 10.hours..14.hours
40
+ boiling: 6.hours..10.hours
41
+ scalding: 3.hours..6.hours
42
+ scorching: 1.hours..3.hours
43
+ searing: 1.second..1.hour
44
+
45
+ brief:
46
+ cold: 3.days..100.years
47
+ hot: 1.second..3.days
48
+
49
+ intensity:
50
+ detailed:
51
+ very_light: 1..5
52
+ light: 5..10
53
+ slightly_moderate: 10..15
54
+ moderate: 15..25
55
+ heavy: 25..50
56
+ very_heavy: 50
57
+
58
+ brief:
59
+ light: 1..5
60
+ moderate: 5..15
61
+ heavy: 15
62
+ # Application Environments
63
+ # ------------------------------------------
64
+
65
+ test:
66
+ <<: *default_settings
67
+
68
+ development:
69
+ <<: *default_settings
70
+
71
+ staging:
72
+ <<: *default_settings
73
+
74
+ production:
75
+ <<: *default_settings
76
+
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :thermometer do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,31 @@
1
+ module Thermometer
2
+ class << self
3
+ def configure
4
+ yield configuration
5
+ end
6
+
7
+ def configuration
8
+ @configuration ||= Configuration.new
9
+ end
10
+ end
11
+ end
12
+
13
+
14
+ require "active_record"
15
+
16
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
17
+
18
+ require 'thermometer/configuration'
19
+ require "thermometer/evaluate"
20
+ require "thermometer/active_record"
21
+ require "thermometer/temperature"
22
+
23
+ $LOAD_PATH.shift
24
+
25
+ #if defined?(ActiveRecord::Base)
26
+ # class ActiveRecord::Base
27
+ # include Thermometer::Temperature
28
+ # end
29
+ #end
30
+
31
+
@@ -0,0 +1,50 @@
1
+ require 'thermometer/evaluate'
2
+
3
+ module Thermometer
4
+ module ActiveRecord
5
+ module RelationMethods
6
+ include Evaluate::Temperatures
7
+
8
+ private
9
+
10
+ def sample_records options
11
+ options = Thermometer.configuration.process_scope_options(proxy_association.reflection.options[:thermometer].merge(options))
12
+
13
+ if options[:limit] && options[:order]
14
+ sample = limit(options[:limit]).order(options[:order]).pluck(options[:date])
15
+ elsif options[:limit] && options[:order].nil?
16
+ sample = limit(options[:limit]).pluck(options[:date])
17
+ elsif options[:limit].nil? && options[:order]
18
+ sample = order(options[:order]).pluck(options[:date])
19
+ else
20
+ sample = pluck(options[:date])
21
+ end
22
+ end
23
+
24
+
25
+ end
26
+ =begin
27
+ module Temperature
28
+
29
+ def
30
+ rel = if ::ActiveRecord::Relation === self
31
+ self
32
+ elsif !defined?(::ActiveRecord::Scoping) or ::ActiveRecord::Scoping::ClassMethods.method_defined? :with_scope
33
+ # Active Record 3
34
+ scoped
35
+ else
36
+ # Active Record 4
37
+ all
38
+ end
39
+
40
+ rel = rel.extending(RelationMethods)
41
+ end
42
+ =end
43
+ end
44
+
45
+ #::ActiveRecord::Base.extend RelationMethods
46
+
47
+ end
48
+
49
+
50
+ #ActiveRecord::Relation.send(:include, Thermometer::ActiveRecord::RelationMethods)
@@ -0,0 +1,116 @@
1
+ module Thermometer
2
+ class Configuration
3
+ def initialize
4
+ @config = YAML.load_file("#{Rails.root.to_s}/config/thermometer.yml")[Rails.env]
5
+ load_time_ranges
6
+ load_scope_options
7
+ end
8
+
9
+ def config
10
+ @config
11
+ end
12
+
13
+ def time_ranges
14
+ @time_ranges
15
+ end
16
+
17
+ ##
18
+ # Defines the date filed in ActiveRecord Model to inspect
19
+ #
20
+ def date
21
+ @date ||= @config['date']
22
+ end
23
+
24
+ ##
25
+ # Defines how many records to sample
26
+ #
27
+ def sample
28
+ @sample ||= @config['sample'].to_i
29
+ end
30
+
31
+ ##
32
+ # Default sort order on Collection
33
+ #
34
+ def order
35
+ unless @order
36
+ raise "'order' must be one of [ASC,DESC]" if !%w(ASC DESC).include?(@config['order'].to_s.upcase)
37
+ end
38
+ @order ||= @config['order']
39
+ end
40
+
41
+ ##
42
+ # If no range is called expclitly, use this one
43
+ #
44
+ def default_time_range
45
+ @default_time_range ||= @time_ranges[@config['time_range']]
46
+ end
47
+
48
+ def scope_options
49
+ @scope_options
50
+ end
51
+
52
+ def process_scope_options options
53
+ processed_options = Hash.new
54
+
55
+ unless options.nil?
56
+
57
+ processed_options[:date] = options.include?(:date) ? options[:date] : self.date #we always need this
58
+
59
+ if options.include?(:explicit)
60
+ #dont use defaults when a scope option is absent
61
+ processed_options[:order] = options.include?(:order) && %w(ASC DESC).include?(options[:order].to_s.upcase) ?
62
+ "#{processed_options[:date]} #{options[:order].to_s.upcase}" : nil
63
+ processed_options[:limit] = options.include?(:sample) ? options[:sample].to_i : nil
64
+ else
65
+ processed_options[:order] = options.include?(:order) && %w(ASC DESC).include?(options[:order].to_s.upcase) ?
66
+ "#{processed_options[:date]} #{options[:order].to_s.upcase}" : "#{processed_options[:date]} #{self.order}"
67
+ processed_options[:limit] = options.include?(:sample) ? options[:sample].to_i : self.sample
68
+
69
+ end
70
+
71
+ processed_options.each do |k,v|
72
+ if v.nil?
73
+ processed_options.delete k
74
+ end
75
+ end
76
+
77
+ else
78
+ processed_options = @scope_options
79
+ end
80
+ return processed_options
81
+ end
82
+
83
+ private
84
+
85
+ def load_scope_options
86
+ @scope_options = Hash.new
87
+ @scope_options[:date] = self.date
88
+ @scope_options[:order] = "#{@scope_options[:date]} #{self.order}"
89
+ @scope_options[:limit] = self.sample
90
+ end
91
+
92
+ ##
93
+ # Load ranges from config file
94
+ #
95
+ def load_time_ranges
96
+ @time_ranges = ActiveSupport::HashWithIndifferentAccess.new
97
+ time_ranges = @config['time']
98
+ time_ranges.each do |t,r|
99
+ time_range = ActiveSupport::HashWithIndifferentAccess.new
100
+ src_ranges ||= r
101
+ src_ranges.map { |k,v| time_range[k.to_sym] = rangify_time_boundaries(v) }
102
+ @time_ranges[t.to_sym] = time_range
103
+ end
104
+
105
+ end
106
+
107
+ ##
108
+ # Takes a string like "2.days..3.weeks"
109
+ # and converts to Range object -> 2.days..3.weeks
110
+ #
111
+ def rangify_time_boundaries(src)
112
+ src.split("..").inject{ |s,e| s.split(".").inject{|n,m| n.to_i.send(m)}..e.split(".").inject{|n,m| n.to_i.send(m) }}
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,87 @@
1
+ module Thermometer
2
+ module Evaluate
3
+ module CalcsForTime
4
+
5
+ def time_diff_for(date, increment=:days, reference=DateTime.now)
6
+ now = Time.at(reference)
7
+ diff = (now - date).to_i
8
+ (diff.to_f/1.send(increment)).send(increment)
9
+ end
10
+
11
+ ##
12
+ # @sample is an array of timestamps
13
+ # @reference is a point in time to evaluate the time_diff
14
+ def average(sample, increment=:days, reference=DateTime.now)
15
+ (sample.map { |s| time_diff_for(s,increment,reference) }.
16
+ inject{ |d,e| d+ e}.to_f / sample.size.send(increment)).send(increment)
17
+ end
18
+
19
+ def min
20
+
21
+ end
22
+
23
+ def max
24
+
25
+ end
26
+ end
27
+
28
+ module Temperatures
29
+ include Thermometer::Evaluate::CalcsForTime
30
+
31
+
32
+ def has_temperature(options={})
33
+ sample = sample_records options
34
+ if sample.size > 1
35
+ evaluate_level(average(sample))
36
+ elsif sample.size == 1
37
+ evaluate_level(time_diff_for(sample.first))
38
+ else
39
+ :none
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Read the direct read_temperature on the instance itself
45
+ #
46
+ def has_temperature?(level,options={})
47
+ level.to_s == has_temperature(options).to_s
48
+ end
49
+
50
+ def is_warmer_than?(level,options={})
51
+ compare_level_to(level,options) do |x,y|
52
+ x > y
53
+ end
54
+ end
55
+
56
+ def is_colder_than?(level,options={})
57
+ compare_level_to(level,options) do |x,y|
58
+ x < y
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def evaluate_level(days, ranges=Thermometer.configuration.default_time_range)
65
+ level = :none
66
+ ranges.each do |k,v|
67
+ if v.include?(days)
68
+ level = k
69
+ break
70
+ end
71
+ end
72
+ return level
73
+ end
74
+
75
+ def compare_level_to(level, options)
76
+ yield Thermometer.configuration.default_time_range[level].max,
77
+ Thermometer.configuration.default_time_range[has_temperature(options)].min
78
+ end
79
+
80
+ def sample_records
81
+ raise "Subclass is responsible for defining this method."
82
+ end
83
+
84
+
85
+ end
86
+ end
87
+ end