Skip to content

Commit

Permalink
basic find and create.
Browse files Browse the repository at this point in the history
  • Loading branch information
nearapogee committed Oct 15, 2012
1 parent 9852510 commit 1c9631a
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 32 deletions.
4 changes: 2 additions & 2 deletions README.txt
Expand Up @@ -28,7 +28,7 @@ class Widget
schema do
name :string
published_at :datetime
image :multipart
image :file
end

middleware do
Expand All @@ -42,7 +42,7 @@ class Widget
end

w = Widget.new(name: "WonderKnife")
w.saved? # => false
w.persisted? # => false
w.save # => true

w.update(name: w.name + "(tm)")
Expand Down
1 change: 1 addition & 0 deletions Rakefile
Expand Up @@ -5,6 +5,7 @@ require 'hoe'

Hoe.plugin :git
Hoe.plugin :isolate
Hoe.plugin :minitest

Hoe.spec 'alke' do
developer("Matt Smith", "matt@nearapogee.com")
Expand Down
108 changes: 91 additions & 17 deletions lib/alke/client.rb
Expand Up @@ -7,34 +7,36 @@ def self.included(receiver)

module ClassMethods
def adapter(adapter = nil)
return @adapter ||= Faraday::Adapter::NetHttp if adapter.nil?
@adapter = adapter
@adapter ||= adapter || Faraday::Adapter::NetHttp
end

def host(host = nil)
return @host.to_s if host.nil?
@host = host
@host ||= host
end

def prefix(prefix = nil)
return @prefix.to_s if prefix.nil?
@prefix = prefix
@prefix ||= prefix
end

def path(path = nil)
return @path.to_s if path.nil?
@path = path
@path ||= path
end

def url(id = nil)
url = prefix + path
url = String.new
url += prefix.to_s
url += path.to_s
url += "/#{id}" if id
url
end

# Public: Create and cache a connection with the default
# middleware stack.
#
# Returns a connection.
def connection
@connection ||= Faraday.new(url: host) do |builder|
# build default stack
builder.use Alke::JsonParser
builder.use adapter
end
@connection.dup
Expand All @@ -43,23 +45,95 @@ def connection
def schema(&block)
parser = Alke::Schema::Parser.new
parser.instance_eval &block
@__schema_raw__ = parser.parsed
@__schema__ = Array.new
@__schema_raw__.each do |attribute, options|
attr_accessor attribute
@__schema__ << "@#{attribute}"
@__parsed_schema__ = parser.parsed
@__write_attributes__ = Array.new
@__parsed_schema__.each do |attribute, options|
@__write_attributes__ << attribute unless options[:readonly] ||
options[:type] == :primary_key
def_attribute attribute, options
end
end

def def_attribute(attribute, options)
case options[:type]
when :datetime
def_datetime(attribute, options)
when :file
# UploadIO
when :primary_key
attr_reader attribute
else
options[:readonly] ? attr_reader(attribute) : \
attr_accessor(attribute)
end
end

def def_datetime(attribute, options)
require 'date'
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{attribute}
if [DateTime, Time, Date].include? datetime.class
return @#{attribute}
end
@#{attribute} = DateTime.parse(@#{attribute}.to_s)
end
RUBY
return if options[:readonly]

class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{attribute}=(datetime)
if [DateTime, Time, Date].include? datetime.class
return @#{attribute} = datetime
end
@#{attribute} = DateTime.parse(datetime.to_s)
end
RUBY
end

def [](id, params = {})
connection.get do |req|
response = connection.get do |req|
req.url url(id), params
end
new(response.body)
end

def create(attributes = {})
new(attributes).tap {|x| x.save }
end
end

module InstanceMethods

def initialize(attributes = {})
unserialize(attributes)
end

def save
response = self.class.connection.post do |req|
req.url self.class.url(id)
req.body = self.writable_attributes
end
unserialize(response.body)
end

def unserialize(data = {})
data.each do |key, value|
singleton_class.send :attr_reader, key unless respond_to?(key)
instance_variable_set "@#{key}", value
end
end

def writable_attributes
attrs = self.class.instance_variable_get("@__write_attributes__")
writable_attributes = Hash.new
attrs.each do |attr|
writable_attributes[attr] = send(attr)
end
writable_attributes
end

def persisted?
!id.nil?
end
end

end
Expand Down
16 changes: 16 additions & 0 deletions lib/alke/json_parser.rb
@@ -0,0 +1,16 @@
require 'json'

module Alke
class JsonParser < Faraday::Middleware
CONTENT_TYPE = 'Content-Type'.freeze
MIME_TYPE = 'application/json'.freeze

def call(env)
env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE
env[:body] = (env[:body] || {}).to_json
@app.call(env).on_complete do
env[:body] = JSON.parse(env[:body])
end
end
end
end
10 changes: 5 additions & 5 deletions lib/alke/schema_builder.rb
Expand Up @@ -3,11 +3,11 @@ module Schema
class Parser < BasicObject
attr_accessor :parsed
def method_missing(*args)
@parsed ||= {}
attribute = args.shift
type = args.shift
options = args
parsed[attribute] = {type: type, options: options}
@parsed ||= {}
attribute = args.shift
type = args.shift
options = args.shift || {}
parsed[attribute] = options.merge(type: type)
end
end
end
Expand Down
9 changes: 7 additions & 2 deletions test/sinatra_app.rb
Expand Up @@ -14,6 +14,7 @@
class Widget < Sequel::Model; plugin :json_serializer, naked: true; end
Widget.find_or_create(name: 'WonderKnife', price: 1000, stock: true, updated_at: Time.utc(2012, 1, 15, 13, 30))

disable :show_exceptions
before '/widget*' do
content_type :json
end
Expand All @@ -32,10 +33,14 @@ class Widget < Sequel::Model; plugin :json_serializer, naked: true; end

post '/widgets/?', provides: :json do
data = JSON.parse(request.body.read)
Widget.create(data['widget'].merge(updated_at: Time.now)).to_json
Widget.create(data.merge(updated_at: Time.now)).to_json
end

put '/widgets/:id', provides: :json do |id|
data = JSON.parse(request.body.read)
Widget[id].update(data['widget'].merge(updated_at: Time.now)).to_json
Widget[id].update(data.merge(updated_at: Time.now)).to_json
end

delete '/widgets/:id', provides: :json do |id|
Widget[id].destroy
end
8 changes: 2 additions & 6 deletions test/test_alke.rb
@@ -1,8 +1,4 @@
require "test/unit"
require "alke"
require 'test_helper'

class TestAlke < Test::Unit::TestCase
def test_sanity
flunk "write tests or I will kneecap you"
end
class TestAlke < MiniTest::Unit::TestCase
end
50 changes: 50 additions & 0 deletions test/test_client.rb
@@ -0,0 +1,50 @@
require 'test_helper'

class TestClient < MiniTest::Unit::TestCase

class Widget
include Alke::Client

host 'http://localhost:4567'
path '/widgets'
end

def setup
@client = Object.new
@class = @client.singleton_class
@class.send :include, Alke::Client
end

def test_default_adapter
assert_equal Faraday::Adapter::NetHttp, @class.adapter
end

def test_default_host
assert_equal nil, @class.host
end

def test_default_prefix
assert_equal nil, @class.prefix
end

def test_default_path
assert_equal nil, @class.path
end

def test_default_url
assert_equal '', @class.url
assert_equal '/1', @class.url(1)
end

def test_set_host
@class.host 'http://localhost:4567'
assert_equal 'http://localhost:4567', @class.host
end

def test_connection
assert @class.connection
assert @class.connection != @class.connection,
"should return a dup'd copy of the connection"
end

end
1 change: 1 addition & 0 deletions test/test_helper.rb
@@ -0,0 +1 @@
require "alke"
70 changes: 70 additions & 0 deletions test/test_integration.rb
@@ -0,0 +1,70 @@
require 'test_helper'

class TestIntegration < MiniTest::Unit::TestCase

class Widget
include Alke::Client

host 'http://localhost:4567'
path '/widgets'

schema do
id :primary_key
name :string
price :integer
stock :boolean
updated_at :datetime, readonly: true
end
end

def setup
$live_warning ||= false
unless ENV['LIVE'] == '1'
unless $live_warning
msg = "\nStart the server `ruby test/sinatra_app.rb` and set "
msg += "the LIVE=1 env variable."
puts msg
$live_warning = true
end
skip
end
end

def test_find
assert w = Widget[1]
assert_equal Widget, w.class
assert_equal 1, w.id
end

def test_responds_to
w = Widget.new
assert w.respond_to? :id
assert w.respond_to? :name
assert w.respond_to? :price
assert w.respond_to? :stock
assert w.respond_to? :updated_at
assert !w.respond_to?(:id=)
assert w.respond_to? :name=
assert w.respond_to? :price=
assert w.respond_to? :stock=
assert !w.respond_to?(:updated_at=)
end

def test_responds_to_schemaless
w = Widget.new quantity: 6
assert w.respond_to? :quantity
assert !w.respond_to?(:quantity=)
w2 = Widget.new
assert !w2.respond_to?(:quantity)
end

def test_create
w = Widget.create(
name: 'WonderRug',
price: 2000,
stock: true
)
assert w.id
assert w.persisted?
end
end

0 comments on commit 1c9631a

Please sign in to comment.