smooth 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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