plyushkin 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -4,8 +4,9 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :test do
7
- gem 'rspec'
8
- gem 'timecop'
9
- gem 'pry-nav'
10
- gem 'sqlite3'
7
+ gem 'rspec', '~> 2.14.1'
8
+ gem 'timecop', '~> 0.7.1'
9
+ gem 'pry-nav', '~> 0.2.3'
10
+ gem 'sqlite3', '~> 1.3.8'
11
+ gem 'webmock', '~> 1.17.1'
11
12
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- plyushkin (0.0.1)
4
+ plyushkin (0.0.3)
5
5
  activerecord (~> 3.2.12)
6
6
 
7
7
  GEM
@@ -18,9 +18,12 @@ GEM
18
18
  activesupport (3.2.16)
19
19
  i18n (~> 0.6, >= 0.6.4)
20
20
  multi_json (~> 1.0)
21
+ addressable (2.3.5)
21
22
  arel (3.0.3)
22
23
  builder (3.0.4)
23
24
  coderay (1.1.0)
25
+ crack (0.4.1)
26
+ safe_yaml (~> 0.9.0)
24
27
  diff-lcs (1.2.5)
25
28
  i18n (0.6.9)
26
29
  method_source (0.8.2)
@@ -40,10 +43,14 @@ GEM
40
43
  rspec-expectations (2.14.4)
41
44
  diff-lcs (>= 1.1.3, < 2.0)
42
45
  rspec-mocks (2.14.4)
46
+ safe_yaml (0.9.7)
43
47
  slop (3.4.7)
44
48
  sqlite3 (1.3.8)
45
49
  timecop (0.7.1)
46
50
  tzinfo (0.3.38)
51
+ webmock (1.17.1)
52
+ addressable (>= 2.2.7)
53
+ crack (>= 0.3.2)
47
54
 
48
55
  PLATFORMS
49
56
  ruby
@@ -51,8 +58,9 @@ PLATFORMS
51
58
  DEPENDENCIES
52
59
  bundler (~> 1.3)
53
60
  plyushkin!
54
- pry-nav
61
+ pry-nav (~> 0.2.3)
55
62
  rake
56
- rspec
57
- sqlite3
58
- timecop
63
+ rspec (~> 2.14.1)
64
+ sqlite3 (~> 1.3.8)
65
+ timecop (~> 0.7.1)
66
+ webmock (~> 1.17.1)
data/lib/plyushkin.rb CHANGED
@@ -9,6 +9,7 @@ require path + "/plyushkin/nil_value"
9
9
  require path + "/plyushkin/property"
10
10
  require path + "/plyushkin/model"
11
11
  require path + "/plyushkin/service"
12
+ require path + "/plyushkin/cache"
12
13
  require path + "/plyushkin/persistence"
13
14
  require path + "/plyushkin/validators/presence"
14
15
  require path + "/plyushkin/core_ext/plyushkin_extensions"
@@ -0,0 +1,14 @@
1
+ module Plyushkin::Cache
2
+ class << self
3
+ def cache
4
+ @cache ||= Plyushkin::Cache::Stub.new
5
+ end
6
+
7
+ def cache=(value)
8
+ @cache = value
9
+ end
10
+ end
11
+ end
12
+
13
+ require File.dirname(File.expand_path(__FILE__)) + "/cache/stub"
14
+ require File.dirname(File.expand_path(__FILE__)) + "/cache/rails_cache"
@@ -0,0 +1,9 @@
1
+ class Plyushkin::Cache::RailsCache
2
+ def read(key)
3
+ Rails.cache.read(key)
4
+ end
5
+
6
+ def write(key, value)
7
+ Rails.cache.write(key, value)
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ class Plyushkin::Cache::Stub
2
+ def initialize
3
+ @cache = {}
4
+ end
5
+
6
+ def write(key, value)
7
+ @cache[key] = value
8
+ end
9
+
10
+ def read(key)
11
+ @cache[key]
12
+ end
13
+
14
+ def clear
15
+ @cache = {}
16
+ end
17
+ end
@@ -12,9 +12,10 @@ ActiveRecord::Base.instance_eval do
12
12
  end
13
13
 
14
14
  def initialize_plyushkin
15
- class << self
15
+ class << self
16
16
  def plyushkin_model
17
- @plyushkin_model ||= Plyushkin::Model.new(Plyushkin::Service::Stub.new)
17
+ model_name = name ? name.parameterize : "record"
18
+ @plyushkin_model ||= Plyushkin::Model.new(Plyushkin::Service.service, model_name, Plyushkin::Cache.cache)
18
19
  end
19
20
  end
20
21
 
@@ -1,10 +1,13 @@
1
1
  class Plyushkin::Model
2
+ attr_reader :service, :name, :cache
2
3
 
3
- def initialize(service)
4
- @service = service
5
- @types = {}
4
+ def initialize(service, name, cache)
5
+ @service = service
6
+ @types = {}
6
7
  @ignore_unchanged_values = {}
7
- @callbacks = {}
8
+ @callbacks = {}
9
+ @name = name
10
+ @cache = cache
8
11
  end
9
12
 
10
13
  def register(name, type, opts={})
@@ -27,9 +30,4 @@ class Plyushkin::Model
27
30
  def ignore_unchanged_values
28
31
  @ignore_unchanged_values.dup
29
32
  end
30
-
31
- def service
32
- @service
33
- end
34
-
35
33
  end
@@ -14,17 +14,18 @@ class Plyushkin::Persistence
14
14
  (@properties || {}).each do |name, property|
15
15
  hash[name] = property.value_hashes
16
16
  end
17
- model.service.put(id, hash.to_json)
17
+ model.service.put(model.name, id, hash)
18
+ model.cache.write(get_key(model.name, id), hash)
18
19
  end
19
20
 
20
21
  def load(id)
21
22
  @properties = {}
22
- model.service.get(id).each do |name, values|
23
+ cached(id).each do |name, values|
23
24
  property = Plyushkin::Property.build(name, model.registered_types[name.to_sym], values,
24
- :callbacks => @callbacks[name.to_sym],
25
- :ignore_unchanged_values => @model.ignore_unchanged_values[name.to_sym] )
25
+ :callbacks => @callbacks[name.to_sym],
26
+ :ignore_unchanged_values => @model.ignore_unchanged_values[name.to_sym] )
26
27
  @properties[name.to_sym] = property
27
- end
28
+ end if id
28
29
  add_missing_properties
29
30
  end
30
31
 
@@ -32,17 +33,30 @@ class Plyushkin::Persistence
32
33
  @callbacks[name] = { callback => block }
33
34
  end
34
35
 
35
- private
36
+ private
36
37
  def model
37
38
  @model
38
39
  end
39
40
 
41
+ def cached(id)
42
+ data = model.cache.read(get_key(model.name, id))
43
+ unless data
44
+ data = model.service.get(model.name, id)
45
+ model.cache.write(get_key(model.name, id), data) if data
46
+ end
47
+ data
48
+ end
49
+
50
+ def get_key(name, id)
51
+ "plyushkin.#{name}.#{id}"
52
+ end
53
+
40
54
  def add_missing_properties
41
55
  (model.registered_types.keys - @properties.keys).each do |name|
42
56
  property = Plyushkin::Property.new(name,
43
- :type => model.registered_types[name],
44
- :callbacks => @callbacks[name],
45
- :ignore_unchanged_values => @model.ignore_unchanged_values[name])
57
+ :type => model.registered_types[name],
58
+ :callbacks => @callbacks[name],
59
+ :ignore_unchanged_values => @model.ignore_unchanged_values[name])
46
60
  @properties[name] = property
47
61
  end
48
62
  end
@@ -1,3 +1,9 @@
1
- module Plyushkin::Service; end
1
+ module Plyushkin::Service
2
+ class << self
3
+ attr_accessor :service
4
+ end
5
+ end
2
6
 
3
7
  require File.dirname(File.expand_path(__FILE__)) + "/service/stub"
8
+ require File.dirname(File.expand_path(__FILE__)) + "/service/web"
9
+
@@ -1,15 +1,24 @@
1
1
  class Plyushkin::Service::Stub
2
2
 
3
3
  def initialize
4
- @store = {}
4
+ @models = {}
5
5
  end
6
6
 
7
- def get(id)
8
- JSON.parse(@store[id] || "{}")
7
+ def get(model, id)
8
+ JSON.parse(get_store(model)[id] || "{}")
9
9
  end
10
10
 
11
- def put(id, json)
12
- @store[id] = json
11
+ def put(model, id, payload)
12
+ get_store(model)[id] = payload.to_json
13
+ end
14
+
15
+ private
16
+ def get_store(model)
17
+ if @models[model]
18
+ store = @models[model]
19
+ else
20
+ store = @models[model] = {}
21
+ end
13
22
  end
14
23
 
15
24
  end
@@ -0,0 +1,35 @@
1
+ require 'net/http'
2
+
3
+ class Plyushkin::Service::Web
4
+ attr_accessor :url
5
+
6
+ def initialize(opts={})
7
+ @url = opts.delete(:url)
8
+ end
9
+
10
+ def get(model, id)
11
+ uri = URI("#{url}/#{model}/#{id}")
12
+ use_ssl = true if uri.scheme == "https"
13
+
14
+ response = Net::HTTP.start(uri.host, uri.port,
15
+ :use_ssl => use_ssl) do |http|
16
+ request = Net::HTTP::Get.new(uri.to_s)
17
+ http.request(request)
18
+ end
19
+
20
+ JSON.parse(response.body)
21
+ end
22
+
23
+ def put(model, id, payload)
24
+ uri = URI("#{url}/#{model}/#{id}")
25
+ use_ssl = true if uri.scheme == "https"
26
+
27
+ response = Net::HTTP.start(uri.host, uri.port,
28
+ :use_ssl => use_ssl) do |http|
29
+ request = Net::HTTP::Put.new(uri.to_s)
30
+ request.body = payload.to_json
31
+ http.request(request)
32
+ end
33
+ end
34
+
35
+ end
@@ -10,6 +10,8 @@ module Plyushkin::Test
10
10
  end
11
11
 
12
12
  class Plyushkin::Test::Member < ActiveRecord::Base; end
13
+ class Plyushkin::Test::WidgetOne < ActiveRecord::Base; hoards :apples; end
14
+ class Plyushkin::Test::WidgetTwo < ActiveRecord::Base; hoards :beans; end
13
15
 
14
16
  class Plyushkin::Test::DateValue < Plyushkin::BaseValue
15
17
  persisted_attr :value, :formatter => :to_date
@@ -1,3 +1,3 @@
1
1
  module Plyushkin
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Plyushkin::Cache::Stub do
4
+ let (:cache) { Plyushkin::Cache::Stub.new }
5
+
6
+ describe "#write" do
7
+ it "should write values to the cache" do
8
+ cache.write("key", "value")
9
+ cache.read("key").should == "value"
10
+ end
11
+ end
12
+
13
+ end
@@ -33,6 +33,20 @@ describe ActiveRecord::Base do
33
33
  property = member.geolocation
34
34
  property.value_type.should == Plyushkin::Test::CoordinateValue
35
35
  end
36
+
37
+ it 'should use class name when setting up the service' do
38
+ widget_one = Plyushkin::Test::WidgetOne.new
39
+ widget_two = Plyushkin::Test::WidgetTwo.new
40
+
41
+ widget_one.apples.create(:value => 1)
42
+ widget_two.beans.create(:value => 2)
43
+
44
+ widget_one.save!
45
+ widget_two.save!
46
+
47
+ widget_one.reload.apples.last.value.should == 1
48
+ widget_two.reload.beans.last.value.should == 2
49
+ end
36
50
  end
37
51
 
38
52
  describe 'after_create option' do
@@ -155,6 +169,5 @@ describe ActiveRecord::Base do
155
169
  member.login_date.last.value.should === now
156
170
  end
157
171
  end
158
-
159
172
  end
160
173
  end
@@ -1,18 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Plyushkin::Persistence do
4
- let(:service) do
5
- service = Plyushkin::Service::Stub.new
6
- service.put(1,
4
+ let(:data) do
7
5
  { :name => [{ :value => "Steve" }] ,
8
6
  :weight => [{ :value => 150 }],
9
7
  :udt => [{ :x => 10, :y => 20 }]
10
- }.to_json)
8
+ }
9
+ end
10
+ let(:service) do
11
+ service = Plyushkin::Service.service
12
+ service.put("widget", 1, data)
11
13
  service
12
14
  end
13
15
 
16
+ let(:cache) { Plyushkin::Cache.cache }
17
+
14
18
  let(:model) do
15
- m = Plyushkin::Model.new(service)
19
+ m = Plyushkin::Model.new(service, "widget", cache)
16
20
  m.register(:name, Plyushkin::StringValue)
17
21
  m.register(:weight, Plyushkin::StringValue)
18
22
  m.register(:udt, Plyushkin::Test::CoordinateValue)
@@ -33,11 +37,10 @@ describe Plyushkin::Persistence do
33
37
  end
34
38
 
35
39
  describe '#save' do
36
- it 'should save to source that load uses' do
40
+ it 'should save to service' do
37
41
  persistence.properties[:name].create(:value => "Mike")
38
42
  persistence.save(1)
39
- persistence.load(1)
40
- persistence.properties[:name].last.value.should == "Mike"
43
+ service.get("widget", 1)["name"].last["value"].should == "Mike"
41
44
  end
42
45
  end
43
46
 
@@ -57,13 +60,13 @@ describe Plyushkin::Persistence do
57
60
  end
58
61
 
59
62
  it 'should parse all history for a property' do
60
- service.put(1,
63
+ service.put("widget", 1,
61
64
  {
62
65
  :name => [
63
66
  { :value => "Ms. Julie Jones", :date => DateTime.now - 2.days },
64
67
  { :value => "Mrs. Julie Smith", :date => DateTime.now - 3.days }
65
68
  ]
66
- }.to_json)
69
+ })
67
70
 
68
71
  property = persistence.properties[:name]
69
72
  property.all.length.should == 2
@@ -76,4 +79,21 @@ describe Plyushkin::Persistence do
76
79
  persistence.properties[:missing_property].all.should == []
77
80
  end
78
81
  end
82
+
83
+ describe "#caching" do
84
+ it "should use cache values on subsequent calls" do
85
+ persistence.properties[:weight].last.value.should == 150
86
+ service.put("widget", 1,
87
+ { :weight => [{ :value => 200 }] })
88
+ persistence.load(1)
89
+ persistence.properties[:weight].last.value.should == 150
90
+ end
91
+
92
+ it "should update the cache on save" do
93
+ persistence.properties[:name].create(:value => "Mike")
94
+ persistence.save(1)
95
+ persistence.load(1)
96
+ persistence.properties[:name].last.value.should == "Mike"
97
+ end
98
+ end
79
99
  end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Plyushkin::Service::Stub do
4
+ let (:service) { Plyushkin::Service::Stub.new }
5
+ let(:document) do
6
+ {
7
+ "weight" => [ {"value" => "5"} ]
8
+ }
9
+ end
10
+
11
+ describe "#get" do
12
+ it 'should get data' do
13
+ service.put("widget", 14, document)
14
+ service.get("widget", 14).should == document
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Plyushkin::Service::Web do
4
+ let(:document) do
5
+ {
6
+ "weight" => [ {"value" => "5"} ]
7
+ }
8
+ end
9
+
10
+ describe '#get' do
11
+ it 'should get json payload from service' do
12
+ stub_request(:get, 'http://plyushkin.com/widget/2').to_return(
13
+ :body => document.to_json)
14
+ service = Plyushkin::Service::Web.new(:url => "http://plyushkin.com")
15
+ service.get("widget", 2).should == document
16
+ end
17
+ end
18
+
19
+ describe '#put' do
20
+ it 'should send json payload' do
21
+ stub_request(:put, 'http://plyushkin.com/widget/2').
22
+ with(:body => document.to_json).
23
+ to_return(:status => 200)
24
+
25
+ service = Plyushkin::Service::Web.new(:url => "http://plyushkin.com")
26
+ service.put("widget", 2, document)
27
+ end
28
+ end
29
+ end
data/spec/spec_helper.rb CHANGED
@@ -5,13 +5,18 @@
5
5
  #
6
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
7
 
8
+ require 'pry'
8
9
  path = File.dirname(File.expand_path(__FILE__))
9
10
  require path + '/../lib/plyushkin'
11
+ Plyushkin::Service.service = Plyushkin::Service::Stub.new
12
+
10
13
  require path + '/../lib/plyushkin/test'
11
14
  Dir[path + "/support/**/*.rb"].each{ |f| require f }
12
15
 
13
16
  require 'timecop'
14
- require 'pry'
17
+ require 'webmock/rspec'
18
+
19
+ WebMock.disable_net_connect!(:allow_localhost => true)
15
20
 
16
21
  RSpec.configure do |config|
17
22
  config.treat_symbols_as_metadata_keys_with_true_values = true
@@ -23,5 +28,9 @@ RSpec.configure do |config|
23
28
  # the seed, which is printed after each run.
24
29
  # --seed 1234
25
30
  config.order = 'random'
31
+
32
+ config.after(:each) do
33
+ Plyushkin::Cache.cache.clear
34
+ end
26
35
  end
27
36
 
@@ -7,6 +7,12 @@ class CreateMembers < ActiveRecord::Migration
7
7
  create_table :members do |t|
8
8
  t.timestamps
9
9
  end
10
+ create_table :widget_ones do |t|
11
+ t.timestamps
12
+ end
13
+ create_table :widget_twos do |t|
14
+ t.timestamps
15
+ end
10
16
  end
11
17
  end
12
18
  CreateMembers.up
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plyushkin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-01-23 00:00:00.000000000 Z
13
+ date: 2014-01-31 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -77,6 +77,9 @@ files:
77
77
  - Rakefile
78
78
  - lib/plyushkin.rb
79
79
  - lib/plyushkin/base_value.rb
80
+ - lib/plyushkin/cache.rb
81
+ - lib/plyushkin/cache/rails_cache.rb
82
+ - lib/plyushkin/cache/stub.rb
80
83
  - lib/plyushkin/core_ext/active_record_base.rb
81
84
  - lib/plyushkin/core_ext/plyushkin_extensions.rb
82
85
  - lib/plyushkin/model.rb
@@ -85,6 +88,7 @@ files:
85
88
  - lib/plyushkin/property.rb
86
89
  - lib/plyushkin/service.rb
87
90
  - lib/plyushkin/service/stub.rb
91
+ - lib/plyushkin/service/web.rb
88
92
  - lib/plyushkin/string_value.rb
89
93
  - lib/plyushkin/test.rb
90
94
  - lib/plyushkin/test/value_types.rb
@@ -92,10 +96,13 @@ files:
92
96
  - lib/plyushkin/version.rb
93
97
  - plyushkin.gemspec
94
98
  - spec/lib/base_value_spec.rb
99
+ - spec/lib/cache/stub_spec.rb
95
100
  - spec/lib/core_ext/active_record_base_spec.rb
96
101
  - spec/lib/nil_value_spec.rb
97
102
  - spec/lib/persistence_spec.rb
98
103
  - spec/lib/property_spec.rb
104
+ - spec/lib/service/stub_spec.rb
105
+ - spec/lib/service/web_spec.rb
99
106
  - spec/lib/value_spec.rb
100
107
  - spec/spec_helper.rb
101
108
  - spec/support/sqlite.rb
@@ -126,10 +133,13 @@ specification_version: 3
126
133
  summary: Plyushkin - the attribute hoarder
127
134
  test_files:
128
135
  - spec/lib/base_value_spec.rb
136
+ - spec/lib/cache/stub_spec.rb
129
137
  - spec/lib/core_ext/active_record_base_spec.rb
130
138
  - spec/lib/nil_value_spec.rb
131
139
  - spec/lib/persistence_spec.rb
132
140
  - spec/lib/property_spec.rb
141
+ - spec/lib/service/stub_spec.rb
142
+ - spec/lib/service/web_spec.rb
133
143
  - spec/lib/value_spec.rb
134
144
  - spec/spec_helper.rb
135
145
  - spec/support/sqlite.rb