smooth 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +24 -0
  7. data/app/assets/images/smooth/.keep +0 -0
  8. data/app/assets/javascripts/smooth/.keep +0 -0
  9. data/app/controllers/.keep +0 -0
  10. data/app/helpers/.keep +0 -0
  11. data/app/mailers/.keep +0 -0
  12. data/app/models/.keep +0 -0
  13. data/app/views/.keep +0 -0
  14. data/config/routes.rb +2 -0
  15. data/lib/smooth.rb +64 -0
  16. data/lib/smooth/api.rb +72 -0
  17. data/lib/smooth/api/policy.rb +18 -0
  18. data/lib/smooth/api/tracking.rb +31 -0
  19. data/lib/smooth/cache.rb +19 -0
  20. data/lib/smooth/command.rb +55 -0
  21. data/lib/smooth/command/instrumented.rb +51 -0
  22. data/lib/smooth/configuration.rb +25 -0
  23. data/lib/smooth/documentation.rb +31 -0
  24. data/lib/smooth/dsl.rb +36 -0
  25. data/lib/smooth/event.rb +27 -0
  26. data/lib/smooth/example.rb +6 -0
  27. data/lib/smooth/ext/core.rb +5 -0
  28. data/lib/smooth/ext/mutations.rb +1 -0
  29. data/lib/smooth/query.rb +62 -0
  30. data/lib/smooth/resource.rb +173 -0
  31. data/lib/smooth/resource/tracking.rb +17 -0
  32. data/lib/smooth/serializer.rb +94 -0
  33. data/lib/smooth/version.rb +3 -0
  34. data/smooth.gemspec +36 -0
  35. data/spec/dummy/README.rdoc +28 -0
  36. data/spec/dummy/Rakefile +6 -0
  37. data/spec/dummy/app/apis/application_api.rb +34 -0
  38. data/spec/dummy/app/assets/images/.keep +0 -0
  39. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  40. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  41. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  42. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  43. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  44. data/spec/dummy/app/mailers/.keep +0 -0
  45. data/spec/dummy/app/models/.keep +0 -0
  46. data/spec/dummy/app/models/author.rb +2 -0
  47. data/spec/dummy/app/models/book.rb +2 -0
  48. data/spec/dummy/app/models/concerns/.keep +0 -0
  49. data/spec/dummy/app/resources/books.rb +103 -0
  50. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  51. data/spec/dummy/bin/bundle +3 -0
  52. data/spec/dummy/bin/rails +4 -0
  53. data/spec/dummy/bin/rake +4 -0
  54. data/spec/dummy/config.ru +4 -0
  55. data/spec/dummy/config/application.rb +30 -0
  56. data/spec/dummy/config/boot.rb +5 -0
  57. data/spec/dummy/config/database.yml +25 -0
  58. data/spec/dummy/config/environment.rb +5 -0
  59. data/spec/dummy/config/environments/development.rb +37 -0
  60. data/spec/dummy/config/environments/production.rb +82 -0
  61. data/spec/dummy/config/environments/test.rb +39 -0
  62. data/spec/dummy/config/initializers/assets.rb +8 -0
  63. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  64. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  65. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  66. data/spec/dummy/config/initializers/inflections.rb +16 -0
  67. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  68. data/spec/dummy/config/initializers/session_store.rb +3 -0
  69. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  70. data/spec/dummy/config/locales/en.yml +23 -0
  71. data/spec/dummy/config/routes.rb +3 -0
  72. data/spec/dummy/config/secrets.yml +22 -0
  73. data/spec/dummy/db/development.sqlite3 +0 -0
  74. data/spec/dummy/db/migrate/20140822065900_create_books.rb +11 -0
  75. data/spec/dummy/db/migrate/20140822065916_create_authors.rb +9 -0
  76. data/spec/dummy/db/schema.rb +30 -0
  77. data/spec/dummy/db/test.sqlite3 +0 -0
  78. data/spec/dummy/lib/assets/.keep +0 -0
  79. data/spec/dummy/log/.keep +0 -0
  80. data/spec/dummy/public/404.html +67 -0
  81. data/spec/dummy/public/422.html +67 -0
  82. data/spec/dummy/public/500.html +66 -0
  83. data/spec/dummy/public/favicon.ico +0 -0
  84. data/spec/lib/smooth/api/policy_spec.rb +5 -0
  85. data/spec/lib/smooth/api_spec.rb +23 -0
  86. data/spec/lib/smooth/cache_spec.rb +8 -0
  87. data/spec/lib/smooth/command_spec.rb +36 -0
  88. data/spec/lib/smooth/configuration_spec.rb +16 -0
  89. data/spec/lib/smooth/event_spec.rb +20 -0
  90. data/spec/lib/smooth/example_spec.rb +5 -0
  91. data/spec/lib/smooth/query_spec.rb +5 -0
  92. data/spec/lib/smooth/resource_spec.rb +38 -0
  93. data/spec/lib/smooth/serializer_spec.rb +26 -0
  94. data/spec/spec_helper.rb +18 -0
  95. metadata +394 -0
@@ -0,0 +1,25 @@
1
+ require 'singleton'
2
+
3
+ module Smooth
4
+ class Configuration
5
+ include Singleton
6
+
7
+ cattr_accessor :query_class,
8
+ :command_class,
9
+ :serializer_class,
10
+ :enable_events
11
+
12
+ @@query_class = Smooth::Query
13
+ @@command_class = Smooth::Command
14
+ @@serializer_class = defined?(ApplicationSerializer) ? ApplicationSerializer : Smooth::Serializer
15
+ @@enable_events = true
16
+
17
+ def enable_event_tracking?
18
+ !!@@enable_events
19
+ end
20
+
21
+ def self.method_missing meth, *args, &block
22
+ instance.send(meth, *args, &block)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ module Smooth
2
+ module Documentation
3
+
4
+ def self.included base
5
+ binding.pry
6
+
7
+ base.class_eval do
8
+ attr_accessor :_inline_description
9
+
10
+ class << self
11
+ attr_accessor :_inline_description
12
+ end
13
+ end
14
+
15
+ base.extend Smooth::Documentation
16
+ end
17
+
18
+ def desc description, *args
19
+ self._inline_description = {
20
+ description: description,
21
+ args: args
22
+ }
23
+ end
24
+
25
+ def inline_description
26
+ val = self._inline_description && self._inline_description.dup
27
+ self._inline_description = nil
28
+ val
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ module Smooth
2
+ module Dsl
3
+ # Creates or opens an API definition
4
+ def api name, *args, &block
5
+ Smooth.current_api_name = name
6
+
7
+ instance = Smooth.fetch_api(name) do |key|
8
+ options = args.dup.extract_options!
9
+
10
+ Smooth::Api.new(name, options).tap do |obj|
11
+ obj.instance_eval(&block) if block_given?
12
+ end
13
+ end
14
+
15
+ instance
16
+ end
17
+
18
+ # Creates or opens a resource definition
19
+ def resource name, *args, &block
20
+ options = args.extract_options!
21
+
22
+ api = case
23
+ when options[:api].is_a?(Symbol) || options[:api].is_a?(String)
24
+ Smooth.fetch_api(options[:api])
25
+ when options[:api].is_a?(Smooth::Api)
26
+ options[:api]
27
+ else
28
+ Smooth.current_api
29
+ end
30
+
31
+ api.resource(name, options, &block)
32
+ end
33
+ end
34
+ end
35
+
36
+ extend(Smooth::Dsl)
@@ -0,0 +1,27 @@
1
+ module Smooth
2
+ class Event < ActiveSupport::Notifications::Event
3
+
4
+ def self.provider
5
+ ActiveSupport::Notifications
6
+ end
7
+
8
+ def payload
9
+ hash = super
10
+ hash && hash.to_mash
11
+ end
12
+
13
+ module Adapter
14
+ def track_event *args, &block
15
+ Smooth::Event.provider.send(:instrument, *args)
16
+ end
17
+
18
+ def subscribe_to event_name, &block
19
+ Smooth::Event.provider.subscribe(event_name) do |*args|
20
+ block.call(Smooth::Event.new(*args), event_name)
21
+ end
22
+ end
23
+ end
24
+
25
+ extend Adapter
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ module Smooth
2
+ class Example
3
+ def self.configure example_description, options, resource=nil
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class Hash
2
+ def to_mash
3
+ Hashie::Mash.new(dup)
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ Mutations.cache_constants = false
@@ -0,0 +1,62 @@
1
+ module Smooth
2
+ class Query
3
+ include Smooth::Documentation
4
+
5
+ class_attribute :query_config
6
+ self.query_config = Hashie::Mash.new(base:{})
7
+
8
+ def self.configure options, resource=nil
9
+ resource ||= Smooth.current_resource
10
+ klass = define_or_open(options, resource)
11
+
12
+ Array(options.blocks).each do |blk|
13
+ klass.class_eval(&blk)
14
+ end
15
+
16
+ klass
17
+ end
18
+
19
+ def self.define_or_open(options, resource)
20
+ resource_name = resource.name
21
+ base = Smooth.query
22
+
23
+ name = options.name
24
+ name = nil if name == "Default"
25
+
26
+ klass = "#{ resource_name }#{ name }".singularize + "Query"
27
+
28
+ if query_klass = Object.const_get(klass) rescue nil
29
+ return query_klass
30
+ end
31
+
32
+ Object.const_set(klass, Class.new(base))
33
+ end
34
+
35
+ def self.start_from *args, &block
36
+ options = args.extract_options!
37
+ config.start_from = options
38
+ end
39
+
40
+ def self.params *args, &block
41
+ options = args.extract_options!
42
+ config.params = options
43
+ end
44
+
45
+ def self.role name, &block
46
+ @current_config = name
47
+ instance_eval(&block) if block_given?
48
+ end
49
+
50
+ def self.config
51
+ val = query_config.send(@current_config || :base)
52
+
53
+ if val.nil?
54
+ val = query_config[@current_config] = {}
55
+ return config
56
+ end
57
+
58
+ val
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,173 @@
1
+ module Smooth
2
+ class Resource
3
+ include Smooth::Documentation
4
+
5
+ attr_accessor :resource_name,
6
+ :api_name
7
+
8
+ # These store the configuration values for the various
9
+ # objects belonging to the resource.
10
+ attr_reader :_queries,
11
+ :_commands,
12
+ :_serializers,
13
+ :_routes,
14
+ :_examples
15
+
16
+ def initialize(resource_name, options={}, &block)
17
+ @resource_name = resource_name
18
+ @options = options
19
+
20
+ @_serializers = {}.to_mash
21
+ @_commands = {}.to_mash
22
+ @_queries = {}.to_mash
23
+ @_routes = {}.to_mash
24
+ @_examples = {}.to_mash
25
+
26
+ @loaded = false
27
+
28
+ instance_eval(&block) if block_given?
29
+
30
+ load!
31
+ end
32
+
33
+ def name
34
+ resource_name
35
+ end
36
+
37
+ def fetch_config object_type, object_key
38
+ source = send("_#{ object_type }s") rescue nil
39
+ source && source.fetch(object_key.to_sym)
40
+ end
41
+
42
+ def fetch object_type, object_key
43
+ source = instance_variable_get("@#{ object_type }s") rescue nil
44
+ source && source.fetch(object_key.to_sym)
45
+ end
46
+
47
+ def loaded?
48
+ !!@loaded
49
+ end
50
+
51
+ def load!
52
+ configure_commands
53
+ configure_serializers
54
+ configure_queries
55
+ configure_routes
56
+ configure_examples
57
+ end
58
+
59
+ def api
60
+ Smooth.fetch_api(api_name || :default)
61
+ end
62
+
63
+ def apply_options *opts
64
+ @options.send(:merge!, *opts)
65
+ end
66
+
67
+ def serializer serializer_name="Default", *args, &block
68
+ if args.empty? && !block_given? && exists = fetch(:serializer, serializer_name)
69
+ return exists
70
+ end
71
+
72
+ options = args.extract_options!
73
+
74
+ description = options.fetch(:description) do
75
+ args.first || inline_description
76
+ end
77
+
78
+ config = _serializers[serializer_name.to_sym] ||= Hashie::Mash.new(options: {}, name: serializer_name, blocks: [block].compact)
79
+ config.description = description unless description.nil?
80
+
81
+ config
82
+ end
83
+
84
+ def command command_name, *args, &block
85
+ if args.empty? && !block_given? && exists = fetch(:command, command_name)
86
+ return exists
87
+ end
88
+
89
+ options = args.extract_options!
90
+
91
+ description = options.fetch(:description) do
92
+ args.first || inline_description
93
+ end
94
+
95
+ config = _commands[command_name.to_sym] ||= Hashie::Mash.new(options: {}, name: command_name, blocks: [block].compact)
96
+
97
+ config.options.merge!(options)
98
+ config.description = description unless description.nil?
99
+
100
+ config
101
+ end
102
+
103
+ def query query_name="Default", *args, &block
104
+ if args.empty? && !block_given? && exists = fetch(:query, query_name)
105
+ return exists
106
+ end
107
+
108
+ options = args.extract_options!
109
+
110
+ description = options.fetch(:description) do
111
+ args.first || inline_description
112
+ end
113
+
114
+ config = _queries[query_name.to_sym] ||= Hashie::Mash.new(options: {}, name: query_name, blocks: [block].compact)
115
+ config.options.merge!(options)
116
+ config.description = description unless description.nil?
117
+
118
+ config
119
+ end
120
+
121
+ def routes &block
122
+ return @routes if !block_given?
123
+ end
124
+
125
+ def examples options={}, &block
126
+ if options.empty? && !block_given?
127
+ return @examples
128
+ end
129
+ end
130
+
131
+ protected
132
+
133
+ def configure_commands
134
+ resource = self
135
+
136
+ @commands = _commands.inject({}.to_mash) do |memo, p|
137
+ ref, cfg = p
138
+ memo[cfg.name] = Smooth::Command.configure(cfg, resource)
139
+ memo
140
+ end
141
+ end
142
+
143
+ def configure_serializers
144
+ resource = self
145
+
146
+ @serializers = _serializers.inject({}.to_mash) do |memo, p|
147
+ ref, cfg = p
148
+ memo[cfg.name] = Smooth::Serializer.configure(cfg, resource)
149
+ memo
150
+ end
151
+ end
152
+
153
+ def configure_queries
154
+ resource = self
155
+
156
+ @queries = _queries.inject({}.to_mash) do |memo, p|
157
+ ref, cfg = p
158
+ memo[cfg.name] = Smooth::Query.configure(cfg, resource)
159
+ memo
160
+ end
161
+ end
162
+
163
+ def configure_routes
164
+ @routes = {}
165
+ end
166
+
167
+ def configure_examples
168
+ @examples = {}
169
+ end
170
+ end
171
+ end
172
+
173
+ require 'smooth/resource/tracking'
@@ -0,0 +1,17 @@
1
+ module Smooth
2
+ class Resource
3
+ module Tracking
4
+ def resources
5
+ @@resources ||= {}
6
+ end
7
+
8
+ def current_resource= resource_object
9
+ @current_resource = resource_object.identifier
10
+ end
11
+
12
+ def current_resource
13
+ resources[@current_resource]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,94 @@
1
+ module Smooth
2
+ class Serializer < ActiveModel::Serializer
3
+ include Smooth::Documentation
4
+
5
+ class_attribute :attribute_descriptions,
6
+ :relationship_descriptions
7
+
8
+ self.attribute_descriptions = {}.to_mash
9
+ self.relationship_descriptions = {}.to_mash
10
+
11
+ def self.method_added method_name
12
+ if documented = inline_description
13
+ attribute_descriptions[method_name.to_sym] = documented
14
+ end
15
+ end
16
+
17
+ def self.schema_attributes
18
+ schema[:attributes]
19
+ end
20
+
21
+ def self.schema_associations
22
+ schema[:associations]
23
+ end
24
+
25
+ def self.configure options, resource=nil
26
+ resource ||= Smooth.current_resource
27
+ klass = define_or_open(options, resource)
28
+
29
+ Array(options.blocks).each do |blk|
30
+ klass.class_eval(&blk)
31
+ end
32
+
33
+ klass
34
+ end
35
+
36
+ def self.define_or_open(options, resource)
37
+ resource_name = resource.name
38
+ base = Smooth.serializer
39
+
40
+ name = options.name
41
+ name = nil if name == "Default"
42
+
43
+ klass = "#{ resource_name }#{ name }".singularize + "Serializer"
44
+
45
+ if serializer_klass = Object.const_get(klass) rescue nil
46
+ return serializer_klass
47
+ end
48
+
49
+ Object.const_set(klass, Class.new(base))
50
+ end
51
+
52
+ def self.documentation_for_attribute attribute
53
+ attribute_descriptions[attribute.to_sym]
54
+ end
55
+
56
+ def self.documentation_for_association association
57
+ relationship_descriptions[association.to_sym]
58
+ end
59
+
60
+ def self.documentation
61
+ attribute_descriptions.merge(relationship_descriptions).to_mash
62
+ end
63
+
64
+ def self.attribute attr, options={}
65
+ documented = inline_description
66
+
67
+ if documented
68
+ attribute_descriptions[attr.to_sym] = documented
69
+ end
70
+
71
+ super
72
+ end
73
+
74
+ def self.has_one attr, options={}
75
+ documented = inline_description
76
+
77
+ if documented
78
+ relationship_descriptions[attr.to_sym] = documented
79
+ end
80
+
81
+ super
82
+ end
83
+
84
+ def self.has_many attr, options={}
85
+ documented = inline_description
86
+
87
+ if documented
88
+ relationship_descriptions[attr.to_sym] = documented
89
+ end
90
+
91
+ super
92
+ end
93
+ end
94
+ end