pact_broker 1.3.2.rc1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/README.md +76 -17
  4. data/db/migrations/16_add_pact_content_foreign_key_to_pacts.rb +6 -0
  5. data/db/migrations/migration_helper.rb +11 -1
  6. data/example/Gemfile +1 -1
  7. data/example/pact_broker_database.sqlite3 +0 -0
  8. data/lib/db.rb +1 -0
  9. data/lib/pact_broker/api.rb +11 -2
  10. data/lib/pact_broker/api/decorators.rb +1 -1
  11. data/lib/pact_broker/api/decorators/base_decorator.rb +3 -3
  12. data/lib/pact_broker/api/decorators/basic_pacticipant_decorator.rb +0 -1
  13. data/lib/pact_broker/api/decorators/embedded_tag_decorator.rb +26 -0
  14. data/lib/pact_broker/api/decorators/embedded_version_decorator.rb +20 -0
  15. data/lib/pact_broker/api/decorators/pact_collection_decorator.rb +9 -3
  16. data/lib/pact_broker/api/decorators/pact_decorator.rb +70 -6
  17. data/lib/pact_broker/api/decorators/pact_pacticipant_decorator.rb +2 -2
  18. data/lib/pact_broker/api/decorators/pacticipant_collection_decorator.rb +4 -28
  19. data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +2 -2
  20. data/lib/pact_broker/api/decorators/representable_pact.rb +4 -1
  21. data/lib/pact_broker/api/decorators/tag_decorator.rb +24 -1
  22. data/lib/pact_broker/api/decorators/timestamps.rb +2 -2
  23. data/lib/pact_broker/api/decorators/version_decorator.rb +27 -3
  24. data/lib/pact_broker/api/decorators/webhooks_decorator.rb +1 -1
  25. data/lib/pact_broker/api/pact_broker_urls.rb +10 -3
  26. data/lib/pact_broker/api/resources/index.rb +5 -5
  27. data/lib/pact_broker/api/resources/pact_content_diff.rb +40 -0
  28. data/lib/pact_broker/api/resources/previous_distinct_pact_version.rb +39 -0
  29. data/lib/pact_broker/api/resources/version.rb +36 -0
  30. data/lib/pact_broker/app.rb +2 -2
  31. data/lib/pact_broker/date_helper.rb +93 -0
  32. data/lib/pact_broker/doc/controllers/app.rb +1 -1
  33. data/lib/pact_broker/doc/views/consumer.markdown +7 -0
  34. data/lib/pact_broker/doc/views/diff-previous-distinct.markdown +5 -0
  35. data/lib/pact_broker/doc/views/latest-pact-version.markdown +7 -0
  36. data/lib/pact_broker/doc/views/latest-pact-versions.markdown +5 -0
  37. data/lib/pact_broker/doc/views/pact-versions.markdown +3 -0
  38. data/lib/pact_broker/doc/views/pact-webhooks.markdown +50 -0
  39. data/lib/pact_broker/doc/views/pacticipants.markdown +2 -0
  40. data/lib/pact_broker/doc/views/provider.markdown +7 -0
  41. data/lib/pact_broker/doc/views/tag-prod-version.markdown +7 -0
  42. data/lib/pact_broker/doc/views/tag-version.markdown +4 -0
  43. data/lib/pact_broker/doc/views/webhooks.markdown +2 -0
  44. data/lib/pact_broker/domain/pact.rb +6 -1
  45. data/lib/pact_broker/domain/tag.rb +0 -1
  46. data/lib/pact_broker/locale/en.yml +37 -1
  47. data/lib/pact_broker/pacts/all_pacts.rb +8 -0
  48. data/lib/pact_broker/pacts/create_formatted_diff.rb +20 -0
  49. data/lib/pact_broker/pacts/diff.rb +105 -0
  50. data/lib/pact_broker/pacts/repository.rb +43 -7
  51. data/lib/pact_broker/repositories/version_repository.rb +1 -0
  52. data/lib/pact_broker/services.rb +6 -1
  53. data/lib/pact_broker/services/pact_service.rb +6 -0
  54. data/lib/pact_broker/services/version_service.rb +2 -2
  55. data/lib/pact_broker/ui.rb +8 -3
  56. data/lib/pact_broker/ui/app.rb +2 -1
  57. data/lib/pact_broker/ui/controllers/base_controller.rb +1 -1
  58. data/lib/pact_broker/ui/controllers/clusters.rb +1 -1
  59. data/lib/pact_broker/ui/views/relationships/show.haml +11 -4
  60. data/lib/pact_broker/version.rb +1 -1
  61. data/pact_broker.gemspec +6 -4
  62. data/public/images/doc-text.svg +1 -0
  63. data/public/stylesheets/relationships.css +43 -1
  64. data/script/publish-2.sh +3 -0
  65. data/script/publish.sh +3 -0
  66. data/spec/features/delete_pact_spec.rb +1 -1
  67. data/spec/features/get_diff.rb +52 -0
  68. data/spec/features/get_previous_distinct_version.rb +51 -0
  69. data/spec/features/get_version.rb +45 -0
  70. data/spec/features/publish_pact_spec.rb +1 -1
  71. data/spec/fixtures/a_consumer-a_provider-2.json +22 -0
  72. data/spec/fixtures/a_consumer-a_provider.json +22 -0
  73. data/spec/fixtures/consumer-provider.json +4 -3
  74. data/spec/integration/app_spec.rb +6 -1
  75. data/spec/lib/pact_broker/api/decorators/embedded_tag_decorator_spec.rb +40 -0
  76. data/spec/lib/pact_broker/api/decorators/embedded_version_decorator_spec.rb +39 -0
  77. data/spec/lib/pact_broker/api/decorators/pact_decorator_spec.rb +52 -10
  78. data/spec/lib/pact_broker/api/decorators/pact_version_decorator_spec.rb +9 -1
  79. data/spec/lib/pact_broker/api/decorators/pacticipant_collection_decorator_spec.rb +36 -0
  80. data/spec/lib/pact_broker/api/decorators/tag_decorator_spec.rb +57 -0
  81. data/spec/lib/pact_broker/api/decorators/version_decorator_spec.rb +49 -0
  82. data/spec/lib/pact_broker/api/decorators/webhooks_decorator_spec.rb +2 -2
  83. data/spec/lib/pact_broker/pacts/create_formatted_diff_spec.rb +33 -0
  84. data/spec/lib/pact_broker/pacts/diff_spec.rb +72 -0
  85. data/spec/lib/pact_broker/pacts/pact_params_spec.rb +2 -2
  86. data/spec/lib/pact_broker/pacts/repository_spec.rb +90 -0
  87. data/spec/lib/pact_broker/repositories/version_repository_spec.rb +10 -2
  88. data/spec/lib/pact_broker/ui/controllers/relationships_spec.rb +1 -1
  89. data/vendor/hal-browser/README.md +6 -0
  90. data/vendor/hal-browser/browser.html +5 -3
  91. data/vendor/hal-browser/js/hal.js +3 -0
  92. data/vendor/hal-browser/js/hal/resource.js +1 -0
  93. data/vendor/hal-browser/js/hal/views/embedded_resource.js +1 -0
  94. data/vendor/hal-browser/js/hal/views/query_uri_dialog.js +12 -2
  95. data/vendor/hal-browser/js/hal/views/resource.js +6 -1
  96. data/vendor/hal-browser/styles.css +10 -0
  97. data/vendor/hal-browser/vendor/js/{jquery-1.9.1.js → jquery-1.10.2.js} +4394 -4202
  98. data/vendor/hal-browser/vendor/js/jquery-1.10.2.min.js +6 -0
  99. data/vendor/hal-browser/vendor/js/jquery-1.10.2.min.map +1 -0
  100. metadata +90 -18
  101. data/db/migrations/15_add_value_to_tag.rb.wip +0 -5
  102. data/lib/pact_broker/doc/views/latest-pacts.markdown +0 -3
@@ -1,5 +1,5 @@
1
1
  require 'pact_broker/api/pact_broker_urls'
2
- require_relative 'version_decorator'
2
+ require_relative 'embedded_version_decorator'
3
3
  require_relative 'base_decorator'
4
4
 
5
5
 
@@ -13,7 +13,7 @@ module PactBroker
13
13
 
14
14
  property :name
15
15
  property :repository_url
16
- property :version, :class => "PactBroker::Domain::Version", :extend => PactBroker::Api::Decorators::VersionRepresenter, :embedded => true
16
+ property :version, :class => "PactBroker::Domain::Version", :extend => PactBroker::Api::Decorators::EmbeddedVersionDecorator, :embedded => true
17
17
 
18
18
  link :self do | options |
19
19
  pacticipant_url(options[:base_url], represented)
@@ -1,26 +1,6 @@
1
- require 'roar/representer/json/hal'
1
+ require 'roar/json/hal'
2
2
  require 'pact_broker/api/pact_broker_urls'
3
- require_relative 'version_decorator'
4
-
5
- module Roar
6
- module Representer
7
- module Feature
8
-
9
- module Hypermedia
10
-
11
- #Monkey patch alert! Get "no method rel for Nil" when there is an empty array
12
- #in links. Cannot reproduce this in the roar tests :(
13
- alias_method :original_compile_links_for, :compile_links_for
14
-
15
- def compile_links_for configs, *args
16
- original_compile_links_for(configs, *args).select(&:any?)
17
- end
18
-
19
- end
20
- end
21
- end
22
- end
23
-
3
+ require_relative 'embedded_version_decorator'
24
4
 
25
5
  module PactBroker
26
6
 
@@ -30,11 +10,7 @@ module PactBroker
30
10
 
31
11
  class PacticipantCollectionRepresenter < BaseDecorator
32
12
 
33
- collection :pacticipants, exec_context: :decorator, :class => PactBroker::Domain::Pacticipant, :extend => PactBroker::Api::Decorators::PacticipantRepresenter
34
-
35
- def pacticipants
36
- represented
37
- end
13
+ collection :entries, :as => :pacticipants, :class => PactBroker::Domain::Pacticipant, :extend => PactBroker::Api::Decorators::PacticipantRepresenter
38
14
 
39
15
  link :self do | options |
40
16
  pacticipants_url options[:base_url]
@@ -47,4 +23,4 @@ module PactBroker
47
23
  end
48
24
  end
49
25
  end
50
- end
26
+ end
@@ -1,5 +1,5 @@
1
1
  require_relative 'base_decorator'
2
- require_relative 'version_decorator'
2
+ require_relative 'embedded_version_decorator'
3
3
  require 'pact_broker/api/decorators/timestamps'
4
4
 
5
5
  module PactBroker
@@ -13,7 +13,7 @@ module PactBroker
13
13
  property :name
14
14
  property :repository_url, as: :repositoryUrl
15
15
 
16
- property :latest_version, as: :'latest-version', :class => PactBroker::Domain::Version, :extend => PactBroker::Api::Decorators::VersionRepresenter, :embedded => true, writeable: false
16
+ property :latest_version, as: :'latest-version', :class => PactBroker::Domain::Version, :extend => PactBroker::Api::Decorators::EmbeddedVersionDecorator, :embedded => true, writeable: false
17
17
 
18
18
  include Timestamps
19
19
 
@@ -1,14 +1,17 @@
1
1
  require 'ostruct'
2
2
 
3
+ # TODO remove this class
4
+
3
5
  module PactBroker
4
6
  module Api
5
7
  module Decorators
6
8
  class RepresentablePact
7
9
 
8
- attr_reader :consumer, :provider, :consumer_version, :created_at, :updated_at
10
+ attr_reader :consumer, :provider, :consumer_version, :consumer_version_number, :created_at, :updated_at
9
11
 
10
12
  def initialize pact
11
13
  @consumer_version = pact.consumer_version
14
+ @consumer_version_number = pact.consumer_version.number
12
15
  @consumer = OpenStruct.new(:version => @consumer_version, :name => pact.consumer.name)
13
16
  @provider = OpenStruct.new(:version => nil, :name => pact.provider.name)
14
17
  @created_at = pact.created_at
@@ -10,10 +10,33 @@ module PactBroker
10
10
 
11
11
  class TagDecorator < BaseDecorator
12
12
 
13
+ property :name
14
+
13
15
  include Timestamps
14
16
 
17
+
15
18
  link :self do | options |
16
- tag_url(options[:base_url], represented)
19
+ {
20
+ title: 'Tag',
21
+ name: represented.name,
22
+ href: tag_url(options[:base_url], represented)
23
+ }
24
+ end
25
+
26
+ link :version do | options |
27
+ {
28
+ title: 'Version',
29
+ name: represented.version.number,
30
+ href: version_url(options.fetch(:base_url), represented.version)
31
+ }
32
+ end
33
+
34
+ link :pacticipant do | options |
35
+ {
36
+ title: 'Pacticipant',
37
+ name: represented.version.pacticipant.name,
38
+ href: pacticipant_url(options.fetch(:base_url), represented.version.pacticipant)
39
+ }
17
40
  end
18
41
 
19
42
  end
@@ -1,4 +1,4 @@
1
- require 'roar/representer/json'
1
+ require 'roar/json'
2
2
 
3
3
  module PactBroker
4
4
 
@@ -8,7 +8,7 @@ module PactBroker
8
8
 
9
9
  module Timestamps
10
10
 
11
- include Roar::Representer::JSON
11
+ include Roar::JSON
12
12
 
13
13
  property :optional_updated_at, as: :updatedAt, exec_context: :decorator, writeable: false
14
14
  property :createdAt, getter: lambda { |_| created_at.xmlschema }, writeable: false
@@ -1,16 +1,40 @@
1
1
  require_relative 'base_decorator'
2
+ require_relative 'embedded_tag_decorator'
2
3
 
3
4
  module PactBroker
4
5
  module Api
5
6
  module Decorators
6
- class VersionRepresenter < BaseDecorator
7
+ class VersionDecorator < BaseDecorator
7
8
 
8
9
  property :number
9
10
 
11
+ collection :tags, embedded: true, :extend => PactBroker::Api::Decorators::EmbeddedTagDecorator
12
+
13
+ link :self do | options |
14
+ {
15
+ title: 'Version',
16
+ name: represented.number,
17
+ href: version_url(options.fetch(:base_url), represented)
18
+ }
19
+ end
20
+
10
21
  link :self do | options |
11
- version_url(options[:base_url], represented)
22
+ {
23
+ title: 'Version',
24
+ name: represented.number,
25
+ href: version_url(options.fetch(:base_url), represented)
26
+ }
12
27
  end
28
+
29
+ link :pacticipant do | options |
30
+ {
31
+ title: 'Pacticipant',
32
+ name: represented.pacticipant.name,
33
+ href: pacticipant_url(options.fetch(:base_url), represented.pacticipant)
34
+ }
35
+ end
36
+
13
37
  end
14
38
  end
15
39
  end
16
- end
40
+ end
@@ -7,7 +7,7 @@ module PactBroker
7
7
  class WebhooksDecorator < BaseDecorator
8
8
 
9
9
 
10
- link :'pb:self' do | context |
10
+ link :self do | context |
11
11
  {
12
12
  title: context[:resource_title],
13
13
  href: context[:resource_url]
@@ -25,8 +25,7 @@ module PactBroker
25
25
  end
26
26
 
27
27
  def pact_url base_url, pact
28
- representable_pact = representable_pact(pact)
29
- "#{pactigration_base_url(base_url, representable_pact)}/version/#{representable_pact.consumer.version.number}"
28
+ "#{pactigration_base_url(base_url, pact)}/version/#{pact.consumer_version_number}"
30
29
  end
31
30
 
32
31
  def pact_url_from_params base_url, params
@@ -48,6 +47,14 @@ module PactBroker
48
47
  "#{base_url}/pacts/provider/#{url_encode(provider_name)}/consumer/#{url_encode(consumer_name)}/versions"
49
48
  end
50
49
 
50
+ def previous_distinct_diff_url pact, base_url
51
+ pact_url(base_url, pact) + "/diff/previous-distinct"
52
+ end
53
+
54
+ def previous_distinct_pact_version_url pact, base_url
55
+ pact_url(base_url, pact) + "/previous-distinct"
56
+ end
57
+
51
58
  def tags_url base_url, version
52
59
  "#{version_url(base_url, version)}/tags"
53
60
  end
@@ -69,7 +76,7 @@ module PactBroker
69
76
  end
70
77
 
71
78
  def webhooks_for_pact_url consumer, provider, base_url
72
- "#{base_url}/webhooks/provider/#{provider.name}/consumer/#{consumer.name}"
79
+ "#{base_url}/webhooks/provider/#{url_encode(provider.name)}/consumer/#{url_encode(consumer.name)}"
73
80
  end
74
81
 
75
82
  def hal_browser_url target_url
@@ -18,22 +18,22 @@ module PactBroker
18
18
  def to_json
19
19
  {
20
20
  _links: {
21
- 'pb:self' =>
21
+ 'self' =>
22
22
  {
23
23
  href: base_url,
24
- title: 'The Pact Broker index page',
24
+ title: 'Index',
25
25
  templated: false
26
26
  },
27
- 'pb:latest-pacts' =>
27
+ 'pb:latest-pact-versions' =>
28
28
  {
29
29
  href: base_url + '/pacts/latest',
30
- title: 'Retrieve latest pacts',
30
+ title: 'Latest pact versions',
31
31
  templated: false
32
32
  },
33
33
  'pb:pacticipants' =>
34
34
  {
35
35
  href: base_url + '/pacticipants',
36
- title: 'Retrieve pacticipants',
36
+ title: 'Pacticipants',
37
37
  templated: false
38
38
  },
39
39
  'pb:webhooks' =>
@@ -0,0 +1,40 @@
1
+ require 'pact_broker/api/resources/base_resource'
2
+ require 'pact_broker/pacts/pact_params'
3
+ require 'pact_broker/pacts/diff'
4
+
5
+ module PactBroker
6
+ module Api
7
+ module Resources
8
+
9
+ class PactContentDiff < BaseResource
10
+
11
+ def content_types_provided
12
+ [["text/plain", :to_text]]
13
+ end
14
+
15
+ def allowed_methods
16
+ ["GET"]
17
+ end
18
+
19
+ def resource_exists?
20
+ pacticipant_service.find_pacticipant_by_name(consumer_name) &&
21
+ pacticipant_service.find_pacticipant_by_name(provider_name)
22
+ end
23
+
24
+ def to_text
25
+ _, body = PactBroker::Pacts::Diff.run pact_params, base_url: base_url
26
+ response.body = body
27
+ end
28
+
29
+ def pact
30
+ @pact ||= pact_service.find_pact(pact_params)
31
+ end
32
+
33
+ def pact_params
34
+ @pact_params ||= PactBroker::Pacts::PactParams.from_request request, path_info
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ require 'pact_broker/api/resources/base_resource'
2
+ require 'pact_broker/pacts/pact_params'
3
+ require 'pact_broker/pacts/diff'
4
+ require 'pact_broker/api/decorators/pact_decorator'
5
+
6
+ module PactBroker
7
+ module Api
8
+ module Resources
9
+
10
+ class PreviousDistinctPactVersion < BaseResource
11
+
12
+ def content_types_provided
13
+ [["application/hal+json", :to_json]]
14
+ end
15
+
16
+ def allowed_methods
17
+ ["GET"]
18
+ end
19
+
20
+ def resource_exists?
21
+ pact
22
+ end
23
+
24
+ def to_json
25
+ PactBroker::Api::Decorators::PactDecorator.new(pact).to_json(base_url: base_url)
26
+ end
27
+
28
+ def pact
29
+ @pact ||= pact_service.find_previous_distinct_pact_version(pact_params)
30
+ end
31
+
32
+ def pact_params
33
+ @pact_params ||= PactBroker::Pacts::PactParams.from_request request, path_info
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ require 'pact_broker/services'
2
+ require 'pact_broker/api/decorators/version_decorator'
3
+
4
+ module PactBroker
5
+ module Api
6
+ module Resources
7
+
8
+ class Version < BaseResource
9
+
10
+ def content_types_provided
11
+ [["application/hal+json", :to_json]]
12
+ end
13
+
14
+ def allowed_methods
15
+ ["GET"]
16
+ end
17
+
18
+ def resource_exists?
19
+ version
20
+ end
21
+
22
+ def to_json
23
+ Decorators::VersionDecorator.new(version).to_json(base_url: base_url)
24
+ end
25
+
26
+ private
27
+
28
+ def version
29
+ @version ||= version_service.find_by_pacticipant_name_and_number path_info
30
+ end
31
+
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -43,7 +43,7 @@ module PactBroker
43
43
  def build_app
44
44
  @app = Rack::Builder.new
45
45
 
46
- @app.use Rack::Static, :urls => ["/stylesheets", "/css", "/fonts", "/js", "/javascripts"], :root => PactBroker.project_root.join("public")
46
+ @app.use Rack::Static, :urls => ["/stylesheets", "/css", "/fonts", "/js", "/javascripts", "/images"], :root => PactBroker.project_root.join("public")
47
47
  @app.use Rack::PactBroker::ConvertFileExtensionToAcceptHeader
48
48
 
49
49
  if configuration.use_hal_browser
@@ -54,7 +54,7 @@ module PactBroker
54
54
  end
55
55
 
56
56
  logger.info "Mounting UI"
57
- require 'pact_broker/ui/app'
57
+ require 'pact_broker/ui'
58
58
 
59
59
  logger.info "Mounting PactBroker::API"
60
60
  require 'pact_broker/api'
@@ -0,0 +1,93 @@
1
+ module PactBroker
2
+ module DateHelper
3
+
4
+ extend self
5
+
6
+ #Ripped from actionview/lib/action_view/helpers/date_helper.rb
7
+
8
+ def local_date_in_words datetime
9
+ datetime.to_time.localtime.to_datetime.strftime("%a %d %b %Y, %l:%M%P %:z").gsub(' ', ' ')
10
+ end
11
+
12
+ class Locale
13
+ def initialize options
14
+ @options = options
15
+ end
16
+
17
+ def t path, options
18
+ I18n.t path, @options.merge(options)
19
+ end
20
+ end
21
+
22
+ def distance_of_time_in_words(from_time, to_time = 0, options = {})
23
+ options = {
24
+ scope: :'datetime.distance_in_words'
25
+ }.merge!(options)
26
+
27
+ from_time = from_time.to_time if from_time.respond_to?(:to_time)
28
+ to_time = to_time.to_time if to_time.respond_to?(:to_time)
29
+ from_time, to_time = to_time, from_time if from_time > to_time
30
+ distance_in_minutes = ((to_time - from_time)/60.0).round
31
+ distance_in_seconds = (to_time - from_time).round
32
+
33
+ # require 'pry'; pry(binding);
34
+
35
+ # locale = I18n.with_options :locale => options[:locale], :scope => options[:scope]
36
+ locale = Locale.new(:locale => options[:locale], :scope => options[:scope])
37
+ case distance_in_minutes
38
+ when 0..1
39
+ return distance_in_minutes == 0 ?
40
+ locale.t(:less_than_x_minutes, :count => 1) :
41
+ locale.t(:x_minutes, :count => distance_in_minutes) unless options[:include_seconds]
42
+
43
+ case distance_in_seconds
44
+ when 0..4 then locale.t :less_than_x_seconds, :count => 5
45
+ when 5..9 then locale.t :less_than_x_seconds, :count => 10
46
+ when 10..19 then locale.t :less_than_x_seconds, :count => 20
47
+ when 20..39 then locale.t :half_a_minute
48
+ when 40..59 then locale.t :less_than_x_minutes, :count => 1
49
+ else locale.t :x_minutes, :count => 1
50
+ end
51
+
52
+ when 2...45 then locale.t :x_minutes, :count => distance_in_minutes
53
+ when 45...90 then locale.t :about_x_hours, :count => 1
54
+ # 90 mins up to 24 hours
55
+ when 90...1440 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
56
+ # 24 hours up to 42 hours
57
+ when 1440...2520 then locale.t :x_days, :count => 1
58
+ # 42 hours up to 30 days
59
+ when 2520...43200 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
60
+ # 30 days up to 60 days
61
+ when 43200...86400 then locale.t :about_x_months, :count => (distance_in_minutes.to_f / 43200.0).round
62
+ # 60 days up to 365 days
63
+ when 86400...525600 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
64
+ else
65
+ if from_time.acts_like?(:time) && to_time.acts_like?(:time)
66
+ fyear = from_time.year
67
+ fyear += 1 if from_time.month >= 3
68
+ tyear = to_time.year
69
+ tyear -= 1 if to_time.month < 3
70
+ leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| Date.leap?(x)}
71
+ minute_offset_for_leap_year = leap_years * 1440
72
+ # Discount the leap year days when calculating year distance.
73
+ # e.g. if there are 20 leap year days between 2 dates having the same day
74
+ # and month then the based on 365 days calculation
75
+ # the distance in years will come out to over 80 years when in written
76
+ # English it would read better as about 80 years.
77
+ minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
78
+ else
79
+ minutes_with_offset = distance_in_minutes
80
+ end
81
+ remainder = (minutes_with_offset % 525600)
82
+ distance_in_years = (minutes_with_offset.div 525600)
83
+ if remainder < 131400
84
+ locale.t(:about_x_years, :count => distance_in_years)
85
+ elsif remainder < 394200
86
+ locale.t(:over_x_years, :count => distance_in_years)
87
+ else
88
+ locale.t(:almost_x_years, :count => distance_in_years + 1)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end