her 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.gitignore +2 -2
- data/.rspec +1 -2
- data/.travis.yml +2 -2
- data/README.md +10 -16
- data/UPGRADE.md +4 -0
- data/examples/grape-and-her/.env.default +3 -0
- data/examples/grape-and-her/Procfile +2 -0
- data/examples/grape-and-her/README.md +27 -0
- data/examples/grape-and-her/api/Gemfile +11 -0
- data/examples/grape-and-her/api/Rakefile +14 -0
- data/examples/grape-and-her/api/app/api.rb +49 -0
- data/examples/grape-and-her/api/app/models/organization.rb +7 -0
- data/examples/grape-and-her/api/app/models/user.rb +9 -0
- data/examples/grape-and-her/api/app/views/organizations/_base.rabl +2 -0
- data/examples/grape-and-her/api/app/views/organizations/index.rabl +3 -0
- data/examples/grape-and-her/api/app/views/organizations/show.rabl +3 -0
- data/examples/grape-and-her/api/app/views/users/_base.rabl +8 -0
- data/examples/grape-and-her/api/app/views/users/index.rabl +3 -0
- data/examples/grape-and-her/api/app/views/users/show.rabl +3 -0
- data/examples/grape-and-her/api/config.ru +5 -0
- data/examples/grape-and-her/api/config/boot.rb +17 -0
- data/examples/grape-and-her/api/config/unicorn.rb +7 -0
- data/examples/grape-and-her/api/db/migrations/001_create_users.rb +11 -0
- data/examples/grape-and-her/api/db/migrations/002_create_organizations.rb +8 -0
- data/examples/grape-and-her/consumer/Gemfile +23 -0
- data/examples/grape-and-her/consumer/app/assets/stylesheets/application.scss +190 -0
- data/examples/grape-and-her/consumer/app/assets/stylesheets/reset.scss +53 -0
- data/examples/grape-and-her/consumer/app/consumer.rb +74 -0
- data/examples/grape-and-her/consumer/app/models/organization.rb +13 -0
- data/examples/grape-and-her/consumer/app/models/user.rb +13 -0
- data/examples/grape-and-her/consumer/app/views/index.haml +9 -0
- data/examples/grape-and-her/consumer/app/views/layout.haml +20 -0
- data/examples/grape-and-her/consumer/app/views/organizations/index.haml +25 -0
- data/examples/grape-and-her/consumer/app/views/organizations/show.haml +11 -0
- data/examples/grape-and-her/consumer/app/views/users/index.haml +33 -0
- data/examples/grape-and-her/consumer/app/views/users/show.haml +9 -0
- data/examples/grape-and-her/consumer/config.ru +20 -0
- data/examples/grape-and-her/consumer/config/boot.rb +30 -0
- data/examples/grape-and-her/consumer/config/unicorn.rb +7 -0
- data/examples/grape-and-her/consumer/lib/response_logger.rb +18 -0
- data/her.gemspec +2 -2
- data/lib/her/model.rb +22 -26
- data/lib/her/model/associations.rb +19 -19
- data/lib/her/model/attributes.rb +173 -0
- data/lib/her/model/base.rb +17 -0
- data/lib/her/model/http.rb +58 -242
- data/lib/her/model/introspection.rb +7 -8
- data/lib/her/model/nested_attributes.rb +3 -3
- data/lib/her/model/orm.rb +15 -205
- data/lib/her/model/parse.rb +86 -0
- data/lib/her/model/paths.rb +54 -14
- data/lib/her/version.rb +1 -1
- data/spec/model/attributes_spec.rb +139 -0
- data/spec/model/dirty_spec.rb +40 -0
- data/spec/model/introspection_spec.rb +5 -5
- data/spec/model/orm_spec.rb +14 -128
- data/spec/model/paths_spec.rb +26 -0
- data/spec/model/validations_spec.rb +17 -0
- data/spec/spec_helper.rb +7 -32
- data/spec/support/extensions/array.rb +5 -0
- data/spec/support/extensions/hash.rb +5 -0
- data/spec/support/macros/model_macros.rb +29 -0
- metadata +52 -15
- data/examples/twitter-oauth/Gemfile +0 -13
- data/examples/twitter-oauth/app.rb +0 -50
- data/examples/twitter-oauth/config.ru +0 -5
- data/examples/twitter-oauth/views/index.haml +0 -9
- data/examples/twitter-search/Gemfile +0 -12
- data/examples/twitter-search/app.rb +0 -55
- data/examples/twitter-search/config.ru +0 -5
- data/examples/twitter-search/views/index.haml +0 -9
@@ -0,0 +1,53 @@
|
|
1
|
+
/* http://meyerweb.com/eric/tools/css/reset/ */
|
2
|
+
/* v1.0 | 20080212 */
|
3
|
+
|
4
|
+
html, body, div, span, applet, object, iframe,
|
5
|
+
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
6
|
+
a, abbr, acronym, address, big, cite, code,
|
7
|
+
del, dfn, em, font, img, ins, kbd, q, s, samp,
|
8
|
+
small, strike, strong, sub, sup, tt, var,
|
9
|
+
b, u, i, center,
|
10
|
+
dl, dt, dd, ol, ul, li,
|
11
|
+
fieldset, form, label, legend,
|
12
|
+
table, caption, tbody, tfoot, thead, tr, th, td {
|
13
|
+
margin: 0;
|
14
|
+
padding: 0;
|
15
|
+
border: 0;
|
16
|
+
outline: 0;
|
17
|
+
font-size: 100%;
|
18
|
+
vertical-align: baseline;
|
19
|
+
background: transparent;
|
20
|
+
}
|
21
|
+
body {
|
22
|
+
line-height: 1;
|
23
|
+
}
|
24
|
+
ol, ul {
|
25
|
+
list-style: none;
|
26
|
+
}
|
27
|
+
blockquote, q {
|
28
|
+
quotes: none;
|
29
|
+
}
|
30
|
+
blockquote:before, blockquote:after,
|
31
|
+
q:before, q:after {
|
32
|
+
content: '';
|
33
|
+
content: none;
|
34
|
+
}
|
35
|
+
|
36
|
+
/* remember to define focus styles! */
|
37
|
+
:focus {
|
38
|
+
outline: 0;
|
39
|
+
}
|
40
|
+
|
41
|
+
/* remember to highlight inserts somehow! */
|
42
|
+
ins {
|
43
|
+
text-decoration: none;
|
44
|
+
}
|
45
|
+
del {
|
46
|
+
text-decoration: line-through;
|
47
|
+
}
|
48
|
+
|
49
|
+
/* tables still need 'cellspacing="0"' in the markup */
|
50
|
+
table {
|
51
|
+
border-collapse: collapse;
|
52
|
+
border-spacing: 0;
|
53
|
+
}
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class Consumer < Sinatra::Base
|
2
|
+
configure do
|
3
|
+
set :root, -> { File.expand_path("./") }
|
4
|
+
set :views, -> { File.join(root, "app/views") }
|
5
|
+
set :haml, :format => :html5, :attr_wrapper => '"', :ugly => true
|
6
|
+
end
|
7
|
+
|
8
|
+
configure :development do
|
9
|
+
register Sinatra::Reloader
|
10
|
+
end
|
11
|
+
|
12
|
+
helpers Sprockets::Helpers
|
13
|
+
|
14
|
+
before do
|
15
|
+
$strio.truncate(0)
|
16
|
+
end
|
17
|
+
|
18
|
+
# GET /
|
19
|
+
get '/' do
|
20
|
+
haml :index
|
21
|
+
end
|
22
|
+
|
23
|
+
# GET /users
|
24
|
+
get '/users' do
|
25
|
+
@users = User.all
|
26
|
+
@user = User.new
|
27
|
+
|
28
|
+
haml :'users/index'
|
29
|
+
end
|
30
|
+
|
31
|
+
# GET /users/:id
|
32
|
+
get '/users/:id' do
|
33
|
+
@user = User.find(params[:id])
|
34
|
+
haml :'users/show'
|
35
|
+
end
|
36
|
+
|
37
|
+
# GET /post
|
38
|
+
post '/users' do
|
39
|
+
@users = User.all
|
40
|
+
@user = User.new(params[:user])
|
41
|
+
|
42
|
+
if @user.save
|
43
|
+
redirect to('/users')
|
44
|
+
else
|
45
|
+
haml :'users/index'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# GET /organizations
|
50
|
+
get '/organizations' do
|
51
|
+
@organizations = Organization.all
|
52
|
+
@organization = Organization.new
|
53
|
+
|
54
|
+
haml :'organizations/index'
|
55
|
+
end
|
56
|
+
|
57
|
+
# GET /organizations/:id
|
58
|
+
get '/organizations/:id' do
|
59
|
+
@organization = Organization.find(params[:id])
|
60
|
+
haml :'organizations/show'
|
61
|
+
end
|
62
|
+
|
63
|
+
# GET /post
|
64
|
+
post '/organizations' do
|
65
|
+
@organizations = Organization.all
|
66
|
+
@organization = Organization.new(params[:organization])
|
67
|
+
|
68
|
+
if @organization.save
|
69
|
+
redirect to('/organizations')
|
70
|
+
else
|
71
|
+
haml :'organizations/index'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
!!! 5
|
2
|
+
%head
|
3
|
+
%title Grape + Her example
|
4
|
+
%link{ rel: "stylesheet", type: "text/css", href: "http://fonts.googleapis.com/css?family=Pacifico" }
|
5
|
+
%link{ type: "text/css", rel: "stylesheet", media: 'screen', href: stylesheet_path("application") }
|
6
|
+
%body
|
7
|
+
#wrap
|
8
|
+
%header#main-header
|
9
|
+
%h1
|
10
|
+
%a{ href: '/' }
|
11
|
+
%strong
|
12
|
+
Her
|
13
|
+
%span
|
14
|
+
Grape + Her example
|
15
|
+
%hr
|
16
|
+
#content
|
17
|
+
= yield
|
18
|
+
%footer#main-footer
|
19
|
+
%pre
|
20
|
+
= $strio.string
|
@@ -0,0 +1,25 @@
|
|
1
|
+
%h2 Create new organization
|
2
|
+
|
3
|
+
%form{ url: "/organizations", method: 'post' }
|
4
|
+
- if @organization.response_errors.any?
|
5
|
+
%ul.errors
|
6
|
+
%p The organization could not be created:
|
7
|
+
%ul
|
8
|
+
- @organization.response_errors.each do |error|
|
9
|
+
%li= error
|
10
|
+
|
11
|
+
%p
|
12
|
+
%label{ for: 'name' } Name
|
13
|
+
%input{ type: 'text', name: 'organization[name]', value: @organization.name, id: 'name' }
|
14
|
+
%p
|
15
|
+
%button{ type: 'submit' } Create
|
16
|
+
|
17
|
+
%hr
|
18
|
+
%h3 Current organizations
|
19
|
+
- if @organizations.any?
|
20
|
+
%ul.records
|
21
|
+
- @organizations.each do |organization|
|
22
|
+
%li
|
23
|
+
%a{ href: "/organizations/#{organization.id}" }= organization.name
|
24
|
+
- else
|
25
|
+
%p.no-records There are currently no organizations.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
%h2 Create new user
|
2
|
+
|
3
|
+
%form{ url: "/users", method: 'post' }
|
4
|
+
- if @user.response_errors.any?
|
5
|
+
.errors
|
6
|
+
%p The user could not be created:
|
7
|
+
%ul
|
8
|
+
- @user.response_errors.each do |error|
|
9
|
+
%li= error
|
10
|
+
|
11
|
+
%p
|
12
|
+
%label{ for: 'email' } Email
|
13
|
+
%input{ type: 'email', name: 'user[email]', value: @user.email, id: 'email' }
|
14
|
+
%p
|
15
|
+
%label{ for: 'fullname' } Full Name
|
16
|
+
%input{ type: 'text', name: 'user[fullname]', value: @user.fullname, id: 'fullname' }
|
17
|
+
%p
|
18
|
+
%label{ for: 'organization_id' } Organization
|
19
|
+
%select{ name: 'user[organization_id]', id: 'organization_id' }
|
20
|
+
- Organization.all.each do |organization|
|
21
|
+
%option{ value: organization.id }= organization.name
|
22
|
+
%p
|
23
|
+
%button{ type: 'submit' } Create
|
24
|
+
|
25
|
+
%hr
|
26
|
+
%h3 Current users
|
27
|
+
- if @users.any?
|
28
|
+
%ul.records
|
29
|
+
- @users.each do |user|
|
30
|
+
%li
|
31
|
+
%a{ href: "/users/#{user.id}" }= user.fullname
|
32
|
+
- else
|
33
|
+
%p.no-records There are currently no users.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path('../config/boot', __FILE__)
|
2
|
+
|
3
|
+
map "/assets" do
|
4
|
+
sprockets = Sprockets::Environment.new.tap do |s|
|
5
|
+
s.append_path File.join(File.dirname(__FILE__), 'app', 'assets', 'stylesheets')
|
6
|
+
s.append_path File.join(File.dirname(__FILE__), 'app', 'assets', 'images')
|
7
|
+
|
8
|
+
Sprockets::Helpers.configure do |config|
|
9
|
+
config.environment = s
|
10
|
+
config.prefix = "/assets"
|
11
|
+
config.digest = true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
run sprockets
|
16
|
+
end
|
17
|
+
|
18
|
+
map "/" do
|
19
|
+
run Consumer
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Bundler setup
|
2
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
3
|
+
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
|
4
|
+
Bundler.require(:default, ENV['RACK_ENV']) if defined? Bundler
|
5
|
+
|
6
|
+
# Require libraries
|
7
|
+
Dir[File.expand_path('../../lib/**/*.rb', __FILE__)].each do |file|
|
8
|
+
dirname = File.dirname(file)
|
9
|
+
file_basename = File.basename(file, File.extname(file))
|
10
|
+
require "#{dirname}/#{file_basename}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Configure Her
|
14
|
+
Her::API.setup :url => "http://0.0.0.0:3100" do |c|
|
15
|
+
c.use Faraday::Request::UrlEncoded
|
16
|
+
c.use Her::Middleware::DefaultParseJSON
|
17
|
+
c.use ResponseBodyLogger, $logger
|
18
|
+
c.use Faraday::Response::Logger, $logger
|
19
|
+
c.use Faraday::Adapter::NetHttp
|
20
|
+
end
|
21
|
+
|
22
|
+
# Require models
|
23
|
+
Dir[File.expand_path('../../app/models/**/*.rb', __FILE__)].each do |file|
|
24
|
+
dirname = File.dirname(file)
|
25
|
+
file_basename = File.basename(file, File.extname(file))
|
26
|
+
require "#{dirname}/#{file_basename}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Application setup
|
30
|
+
require File.expand_path('../../app/consumer', __FILE__)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
class ResponseBodyLogger < Faraday::Response::Middleware
|
5
|
+
def initialize(app, logger)
|
6
|
+
@app = app
|
7
|
+
@logger = logger
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_complete(env)
|
11
|
+
@logger.info ""
|
12
|
+
@logger.info env[:body]
|
13
|
+
@logger.info "\n"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
$strio = StringIO.new
|
18
|
+
$logger = Logger.new($strio).tap { |logger| logger.formatter = proc { |severity, datetime, progname, msg| "#{msg}\n" } }
|
data/her.gemspec
CHANGED
@@ -19,10 +19,10 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
s.add_development_dependency "rake", "~> 10.0"
|
21
21
|
s.add_development_dependency "rspec", "~> 2.13"
|
22
|
-
s.add_development_dependency "
|
22
|
+
s.add_development_dependency "fivemat", "~> 1.2"
|
23
23
|
|
24
24
|
s.add_runtime_dependency "activemodel", ">= 3.0.0"
|
25
25
|
s.add_runtime_dependency "activesupport", ">= 3.0.0"
|
26
26
|
s.add_runtime_dependency "faraday", "~> 0.8"
|
27
|
-
s.add_runtime_dependency "multi_json", "~> 1.
|
27
|
+
s.add_runtime_dependency "multi_json", "~> 1.7"
|
28
28
|
end
|
data/lib/her/model.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require "her/model/base"
|
2
2
|
require "her/model/http"
|
3
|
+
require "her/model/attributes"
|
3
4
|
require "her/model/orm"
|
5
|
+
require "her/model/parse"
|
4
6
|
require "her/model/associations"
|
5
7
|
require "her/model/introspection"
|
6
8
|
require "her/model/paths"
|
@@ -21,48 +23,42 @@ module Her
|
|
21
23
|
module Model
|
22
24
|
extend ActiveSupport::Concern
|
23
25
|
|
24
|
-
#
|
26
|
+
# Her modules
|
27
|
+
include Her::Model::Base
|
28
|
+
include Her::Model::Attributes
|
25
29
|
include Her::Model::ORM
|
30
|
+
include Her::Model::HTTP
|
31
|
+
include Her::Model::Parse
|
26
32
|
include Her::Model::Introspection
|
27
33
|
include Her::Model::Paths
|
28
34
|
include Her::Model::Associations
|
29
35
|
include Her::Model::NestedAttributes
|
36
|
+
|
37
|
+
# Supported ActiveModel modules
|
30
38
|
include ActiveModel::Validations
|
31
39
|
include ActiveModel::Conversion
|
32
40
|
include ActiveModel::Dirty
|
41
|
+
include ActiveModel::Naming
|
42
|
+
include ActiveModel::Translation
|
33
43
|
|
34
44
|
# Class methods
|
35
45
|
included do
|
36
|
-
|
37
|
-
|
38
|
-
extend ActiveModel::Naming
|
39
|
-
extend ActiveModel::Translation
|
46
|
+
# Define the root element name, used when `parse_root_in_json` is set to `true`
|
47
|
+
root_element self.name.split("::").last.underscore.to_sym
|
40
48
|
|
41
|
-
|
42
|
-
|
49
|
+
# Define resource and collection paths
|
50
|
+
collection_path "#{root_element.to_s.pluralize}"
|
51
|
+
resource_path "#{root_element.to_s.pluralize}/:id"
|
43
52
|
|
44
|
-
#
|
45
|
-
root_element self.name.split("::").last.underscore
|
46
|
-
base_path = root_element.pluralize
|
47
|
-
collection_path "#{base_path}"
|
48
|
-
resource_path "#{base_path}/:id"
|
53
|
+
# Assign the default API
|
49
54
|
uses_api Her::API.default_api
|
50
|
-
end
|
51
55
|
|
52
|
-
|
53
|
-
|
54
|
-
# * an association
|
55
|
-
def has_key?(attribute_name)
|
56
|
-
has_data?(attribute_name) ||
|
57
|
-
has_association?(attribute_name)
|
58
|
-
end
|
56
|
+
# Define the default primary key
|
57
|
+
primary_key :id
|
59
58
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
def [](attribute_name)
|
64
|
-
get_data(attribute_name) ||
|
65
|
-
get_association(attribute_name)
|
59
|
+
# Configure ActiveModel callbacks
|
60
|
+
extend ActiveModel::Callbacks
|
61
|
+
define_model_callbacks :create, :update, :save, :find, :destroy
|
66
62
|
end
|
67
63
|
end
|
68
64
|
end
|