mirror_mirror 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mirror_mirror.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ New BSD License
2
+ Copyright (c) 1999 - 2012, Daniel Doezema
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
+ * The names of the contributors and/or copyright holder may not be used to endorse or promote products derived from this software without specific prior written permission.
10
+
11
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DANIEL DOEZEMA BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # MirrorMirror
2
+
3
+ A Ruby gem that allows for easier interactions and integration with external REST resources.
4
+
5
+ ## Installation
6
+
7
+ gem 'mirror_mirror'
8
+
9
+ ## Usage Example
10
+
11
+ ### Environment
12
+
13
+ # App controlled resource, reliant on external resource.
14
+ class Timesheet < ActiveRecord::Base
15
+ belongs_to :contractor, :auto_reflect => true
16
+ end
17
+
18
+ # Extenal Resource
19
+ class Contractor < ActiveRecord::Base
20
+ has_many :timesheets
21
+
22
+ mirror_mirror "http://api.example.com/v1/contractors",
23
+ :request => :resource_request, :find => true
24
+
25
+ def resource_request(verb, url, params)
26
+ response = RestClient.send(verb, url + '.json', params)
27
+ ActiveSupport::JSON.decode(response.to_str)["result"] if response.code == 200
28
+ end
29
+ end
30
+
31
+ # API Resource Example
32
+ {
33
+ result: [{
34
+ id: 1,
35
+ first_name: "Daniel",
36
+ last_name: "Doezema"
37
+ },
38
+ {
39
+ id: 2,
40
+ first_name: "John",
41
+ last_name: "Doe"
42
+ }]
43
+ }
44
+
45
+ ### External API Scenario
46
+
47
+ class ContractorsController < ApiController
48
+ # api/contractors/:id/clock_out
49
+ def clock_out
50
+ # Automatically found & created b/c of the mirror_mirror :find => true option
51
+ contractor = Contractor.find(params[:id])
52
+ timesheet = contractor.timesheets.last
53
+ result = timesheet.update_attributes(:ended_at => Time.now)
54
+ end
55
+ end
56
+
57
+ ### The "We've only got an id" Scenario
58
+
59
+ In this scenario a `contractor_id` is present, but we don't know if the `Contractor` record exists locally yet -- this is where the `:auto_reflect` option helps out. If a local `Contractor` record is not found then a request will automatically be made to fetch and create it.
60
+
61
+ # Controller
62
+ @timesheets = Timesheet.where("started_at > ?", Time.now.beginning_of_day)
63
+
64
+ # View
65
+ <h1>Today's Punched-In Contractors</h1>
66
+ <ul>
67
+ <% @timesheets.each do |timesheet| %>
68
+ <li><%= "#{timesheet.worker.first_name} #{timesheet.worker.last_name}"</li>
69
+ <% end %>
70
+ </ul>
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,136 @@
1
+ module MirrorMirror::ActiveRecordBase
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+
6
+ def reflect!
7
+ hash = self.class.mirror_request!(:get, mirror_url)
8
+ raise RecordNotFound, "Non-Hash returned from resource: #{mirror_url} => #{hash.inspect}" unless hash.is_a?(Hash)
9
+ raise RecordNotFound, "Empty hash returned from resource." unless hash.any?
10
+ hash.each {|k,v| send("#{k}=", v) if respond_to?("#{k}=") }
11
+ self.updated_at = Time.now
12
+ save!
13
+ self
14
+ end
15
+
16
+ def mirror_url
17
+ self.class.mirror_url(id)
18
+ end
19
+
20
+ end
21
+
22
+ module ClassMethods
23
+
24
+ def mirror_mirror(*args)
25
+ options = args.extract_options!
26
+ raise ArgumentError, "url is blank." if args[0].blank?
27
+ options[:url] = args[0]
28
+ @mirror_mirrior_options = options
29
+ end
30
+
31
+ def mirroring?
32
+ @mirror_mirrior_options.present?
33
+ end
34
+
35
+ def mirror_mirror_options(option)
36
+ @mirror_mirrior_options ||= {}
37
+ @mirror_mirrior_options[option]
38
+ end
39
+
40
+ def mirror_find?
41
+ mirroring? ? @mirror_mirrior_options[:find] : false
42
+ end
43
+
44
+ def mirror_url(id = nil)
45
+ url = @mirror_mirrior_options[:url]
46
+ url += "/#{id.to_i}" if id.to_i > 0
47
+ url
48
+ end
49
+
50
+ def mirror_request!(verb, url, params = {})
51
+ method = @mirror_mirrior_options[:request]
52
+ if method.present?
53
+ raise ArgumentError, "url is blank." if url.blank?
54
+ raise ArgumentError, "verb is invalid." unless verb.in?([:get, :put, :post, :delete])
55
+ raise ArgumentError, "params is not a Hash." unless params.is_a?(Hash)
56
+ begin
57
+ send(method, verb, url, params)
58
+ rescue => e
59
+ raise FailedRequest, "#{verb.to_s.upcase} #{url} :: Params: #{params} :: Initial Exception: #{e.class.name}: #{e}"
60
+ end
61
+ elsif MirrorMirror.request?
62
+ MirrorMirror.request(verb, url, params)
63
+ else
64
+ raise FailedRequest, "No request method specified."
65
+ end
66
+ end
67
+
68
+ def reflect_all!(params = {})
69
+ array = mirror_request(:get, mirror_url, params)
70
+ raise RecordNotFound, "Non-Array returned from resource: #{mirror_url} => #{hash.inspect}" unless array.is_a?(Array)
71
+ if array.any?
72
+ transaction do
73
+ array.each do |hash|
74
+ if (id = hash["id"]).present?
75
+ record = find_or_initialize_by_id(id)
76
+ hash.each {|k,v| record.send("#{k}=", v) if record.respond_to?("#{k}=") }
77
+ end
78
+ record.updated_at = Time.now
79
+ record.save!
80
+ end
81
+ end
82
+ end
83
+ all
84
+ end
85
+
86
+ # Active Record Polymorphing
87
+ def find(*args)
88
+ return super unless mirror_find?
89
+ begin
90
+ super
91
+ rescue ActiveRecord::RecordNotFound
92
+ if (id = args[0].to_i) > 0
93
+ record = self.new
94
+ record.id = id
95
+ record.reflect!
96
+ end
97
+ end
98
+ end
99
+
100
+ def reflect_by_id!(id)
101
+ record = self.new
102
+ record.id = id
103
+ record.reflect!
104
+ record
105
+ end
106
+
107
+ def find_or_reflect_by_id!(*args)
108
+ record = find_or_initialize_by_id(*args)
109
+ record.reflect! if record.new_record?
110
+ record
111
+ end
112
+
113
+ def belongs_to(*args)
114
+ options = args.extract_options!
115
+ association_name = args[0].to_s
116
+ options.delete(:auto_reflect) if auto_reflect = options[:auto_reflect].presence
117
+ if auto_reflect
118
+ self.class_eval <<-RUBY
119
+ def #{association_name}
120
+ if (result = super).blank?
121
+ model = send("#{association_name}_type").constantize
122
+ id = send("#{association_name}_id")
123
+ if id.present? && model.mirroring?
124
+ model.reflect_by_id!(id)
125
+ result = super(true)
126
+ end
127
+ end
128
+ result
129
+ end
130
+ RUBY
131
+ end
132
+ super(*(args << options))
133
+ end
134
+
135
+ end
136
+ end
@@ -0,0 +1,11 @@
1
+ module MirrorMirror
2
+ class Railtie < Rails::Railtie
3
+
4
+ initializer 'mirror_mirror.active_record_base' do |app|
5
+ ActiveSupport.on_load :active_record do
6
+ include MirrorMirror::ActiveRecordBase
7
+ end
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module MirrorMirror
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,30 @@
1
+ require "mirror_mirror/version"
2
+
3
+ module MirrorMirror
4
+ autoload :ActiveRecordBase, 'mirror_mirror/active_record_base'
5
+
6
+ class RecordNotFound < ::StandardError; end
7
+ class FailedRequest < ::StandardError; end
8
+
9
+ class << self
10
+ def request!(verb = :get, url = nil, params = {}, &block)
11
+ unless block_given?
12
+ raise ArgumentError, "url is blank." if url.blank?
13
+ raise ArgumentError, "verb is invalid." unless verb.in?([:get, :put, :post, :delete])
14
+ raise ArgumentError, "params is not a Hash." unless params.is_a?(Hash)
15
+ begin
16
+ return @request_proc.call(verb, url, params)
17
+ rescue => e
18
+ raise FailedRequest, "#{verb.to_s.upcase} #{url} :: Params: #{params} :: Initial Exception: #{e.class.name}: #{e}"
19
+ end
20
+ end
21
+ @request_proc = Proc.new(&block)
22
+ end
23
+
24
+ def request?
25
+ @request_proc.present?
26
+ end
27
+ end
28
+ end
29
+
30
+ require 'mirror_mirror/railtie' if defined?(Rails)
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/mirror_mirror/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Daniel Doezema"]
6
+ gem.email = ["daniel.doezema@gmail.com"]
7
+ gem.description = %q{Allows for easier interactions and integration with external REST resources.}
8
+ gem.summary = %q{Allows for easier interactions and integration with external REST resources.}
9
+ gem.homepage = "https://github.com/veloper/mirror_mirror"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "mirror_mirror"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = MirrorMirror::VERSION
17
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mirror_mirror
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Daniel Doezema
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-06-17 00:00:00 Z
14
+ dependencies: []
15
+
16
+ description: Allows for easier interactions and integration with external REST resources.
17
+ email:
18
+ - daniel.doezema@gmail.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - .gitignore
27
+ - Gemfile
28
+ - LICENSE
29
+ - README.md
30
+ - Rakefile
31
+ - lib/mirror_mirror.rb
32
+ - lib/mirror_mirror/active_record_base.rb
33
+ - lib/mirror_mirror/railtie.rb
34
+ - lib/mirror_mirror/version.rb
35
+ - mirror_mirror.gemspec
36
+ homepage: https://github.com/veloper/mirror_mirror
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.8.24
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: Allows for easier interactions and integration with external REST resources.
63
+ test_files: []
64
+